C # හි විචල්‍යය පුරෝකථනයක නැවත භාවිතා කිරීමට හේතුවක් තිබේද?


1692

C # හි ලැම්බඩා ප්‍රකාශන හෝ නිර්නාමික ක්‍රම භාවිතා කරන විට, නවීකරණය කරන ලද වසා දැමීමේ වළල්ලට ප්‍රවේශ වීම ගැන අප සැලකිලිමත් විය යුතුය . උදාහරණයක් වශයෙන්:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

නවීකරණය කරන ලද වසා දැමීම හේතුවෙන්, ඉහත කේතය මඟින් Whereවිමසුමේ ඇති සියලුම වගන්තිවල අවසාන අගය මත පදනම් වේ s.

ලෙස පැහැදිලි මෙතන , මේ සිදු වන නිසා sවිචල්ය දී ප්රකාශයට පත් කරන foreachසම්පාදකවරයා මේ වගේ පරිවර්ථනය කර ඇත ඉහත පුඩුවක්:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

මේ වෙනුවට:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

මෙහි පෙන්වා දී ඇති පරිදි , ලූපයෙන් පිටත විචල්‍යයක් ප්‍රකාශ කිරීමේ කාර්ය සාධන වාසි නොමැති අතර සාමාන්‍ය තත්වයන් යටතේ මෙය කිරීමට මට සිතිය හැකි එකම හේතුව වන්නේ ඔබ ලූපයේ විෂය පථයෙන් පිටත විචල්‍යය භාවිතා කිරීමට අදහස් කරන්නේ නම්:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

කෙසේ වෙතත්, ලූපයක අර්ථ දක්වා ඇති විචල්‍යයන් ලූපයෙන් foreachපිටත භාවිතා කළ නොහැක:

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

එබැවින් සම්පාදකයා විසින් විචල්‍යය ප්‍රකාශයට පත් කරනුයේ එය බොහෝ විට සොයාගත හැකි හා නිදොස්කරණය කිරීමට අපහසු වන දෝෂයකට බෙහෙවින් නැඹුරු වන අතර කිසිදු ප්‍රතිලාභයක් නොලැබෙන බැවිනි.

ඔබ සමඟ කළ හැකි යමක් තිබේද foreachඔබ ඔවුන් අභ්යන්තර-scoped විචල්ය සමඟ සම්පාදනය නොහැකි නම්, බව මේ ආකාරයට වළළු, හෝ මෙම නිර්නාමික ක්රම සහ ලැම්ඩා ප්රකාශන ලබා ගත හැකි හෝ පොදු විය පෙර සිදු වූ බව පමණක් හිතුවක්කාරී තේරීම වන අතර, වන hasn එතැන් සිට සංශෝධනය කර නැද්ද?


4
මොකක්ද වැරැද්ද String s; foreach (s in strings) { ... }?
බ්‍රැඩ් ක්‍රිස්ටි

5
Rad බ්‍රැඩ්ක්‍රිස්ටි ඕපී ඇත්ත වශයෙන්ම කතා කරන්නේ foreachලැම්ඩා ප්‍රකාශන ගැන නොවන අතර OP පෙන්වා ඇති ආකාරයට සමාන කේතයක් ඇතිවේ ...
යාහියා

22
Rad බ්‍රැඩ් ක්‍රිස්ටි: එය සම්පාදනය කරනවාද? ( දෝෂය: මා සඳහා පුරෝකථන ප්‍රකාශයක වර්ගය සහ හඳුනාගැනීම අවශ්‍ය වේ )
ඔස්ටින් සැලොනන්

32
Ak ජාකොබ් බොට්ස්නිල්සන්: එය ලැම්බඩා පිටත වසා ඇති දේශීය ප්‍රදේශයකි; ඇයි ඔබ උපකල්පනය කරන්නේ එය කිසිසේත්ම තොගයේ පවතිනු ඇති බවයි. එහි ආයු කාලය තොග රාමුවට වඩා දිගු ය !
එරික් ලිපර්ට්

3
Ric එරික්ලිපර්ට්: මම ව්‍යාකූලයි. ලැම්බඩා විසින් foreach විචල්‍යය වෙත යොමු කිරීමක් ග්‍රහණය කර ගන්නා බව මට වැටහී ඇත (එය අභ්‍යන්තරව ලූපයෙන් පිටත ප්‍රකාශයට පත් කර ඇත) එබැවින් ඔබ එහි අවසාන අගයට සාපේක්ෂව සැසඳේ. මට ලැබෙනවා කියලා. මට නොතේරෙන දෙය නම් ලූපය තුළ විචල්‍යය ප්‍රකාශ කිරීමෙන් කිසියම් වෙනසක් සිදු වන්නේ කෙසේද යන්නයි . සම්පාදක-ලේඛක දෘෂ්ටි කෝණයකින්, ප්‍රකාශය ලූපය ඇතුළත හෝ පිටත තිබේද යන්න නොසලකා මම එක වර්‍ගයක් (var 's) තොගයට වෙන් කරමි. සෑම පුනරාවර්තනයක්ම නව සඳහනක් තොගයට තල්ලු කිරීමට මට අවශ්‍ය නොවනු ඇත!
ඇන්තනි

Answers:


1413

සම්පාදකයා විසින් විචල්‍යය ප්‍රකාශයට පත් කරන්නේ එය බොහෝ විට සොයා ගැනීමට සහ නිදොස් කිරීමට අපහසු වන දෝෂයකට බෙහෙවින් නැඹුරු වන අතර, නොපෙනෙන ප්‍රතිලාභයක් නොලැබෙන බැවිනි.

ඔබේ විවේචනය මුළුමනින්ම යුක්ති සහගත ය.

මම මෙම ගැටලුව මෙහි විස්තරාත්මකව සාකච්ඡා කරමි:

හානිකර යැයි සැලකෙන ලූප් විචල්‍යය වසා දැමීම

Foreach loops මඟින් ඔබට අභ්‍යන්තර පරිමාණ විචල්‍යයක් සමඟ සම්පාදනය කර ඇත්නම් ඔබට කළ නොහැකි දෙයක් තිබේද? නැතහොත් මෙය නිර්නාමික ක්‍රම සහ ලැම්බඩා ප්‍රකාශන ලබා ගැනීමට පෙර හෝ පොදු වූ අතර එතැන් සිට සංශෝධනය නොකළ අත්තනෝමතික තේරීමක් ද?

පසු. සී # 1.0 පිරිවිතරයේ ඇත්ත වශයෙන්ම ලූප් විචල්‍යය ලූප් ශරීරය තුළ හෝ පිටත තිබේද යන්න නොකියයි. C # 2.0 හි සංවෘත අර්ථකථන හඳුන්වා දුන් විට, "for" පුඩුවට අනුකූලව ලූප විචල්‍යය ලූපයෙන් පිටත තැබීමට තෝරා ගන්නා ලදී.

සියලු දෙනා එම තීරණය ගැන කනගාටු වන බව පැවසීම සාධාරණ යැයි මම සිතමි. මෙය C # හි ඇති නරකම "ගොචා" වලින් එකක් වන අතර, එය නිවැරදි කිරීම සඳහා අපි බිඳීමේ වෙනස ගැනීමට යන්නෙමු. C # 5 දී foreach ලූපය විචල්ය තර්කානුකූලව වනු ඇත තුල කම්බියක් ශරීරය, එම නිසා වසා නැවුම් පිටපතක් සෑම අවස්ථාවකදීම ලැබෙනු ඇත.

මෙම forලූපය වෙනස් කරනු ලබන්නේ නැත, සහ වෙනස් C # හි පෙර අනුවාදයකින් කිරීමට "නැවත ප්රදර්ශනය කරනු ලබන්නේ" කළ නොහැකි වනු ඇත. එබැවින් මෙම මෝඩකම භාවිතා කිරීමේදී ඔබ දිගටම ප්‍රවේශම් විය යුතුය.


179
ඇත්ත වශයෙන්ම අපි සී # 3 සහ සී # 4 හි මෙම වෙනස පසුපසට තල්ලු කළෙමු. අපි සී # 3 නිර්මාණය කරන විට අපට වැටහී ගියේ (සී # 2 හි දැනටමත් පැවති) ගැටලුව වඩාත් නරක අතට හැරෙනු ඇත්තේ ලැම්බඩා විශාල ප්‍රමාණයක් ඇති නිසා (සහ විමසුම LINQ ට ස්තූතිවන්ත වන පරිදි foreach loops හි වෙස්වළාගෙන ඇති lambdas). C # 3 හි නිවැරදි කිරීමට වඩා ප්‍රමාද වී එය නිවැරදි කිරීමට වරෙන්තුවක් ලබා ගැනීම සඳහා ගැටලුව ප්‍රමාණවත් තරම් නරක වන තෙක් අපි බලා සිටීම ගැන මම කනගාටු වෙමි.
එරික් ලිපර්ට්

75
දැන් අපට මතක තබා ගත යුතු foreachවන්නේ 'ආරක්ෂිතයි' නමුත් forඑසේ නොවේ.
leppie

22
ichmichielvoo: වෙනස බිඳී යන්නේ එය පසුගාමී අනුකූල නොවේ යන අර්ථයෙන් ය. පැරණි සම්පාදකයක් භාවිතා කරන විට නව කේතය නිවැරදිව ක්‍රියාත්මක නොවේ.
ලෙපී

41
En බෙන්ජෝල්: නැහැ, ඒ නිසා අපි එය ගැනීමට කැමති. ජෝන් ස්කීට් මට පෙන්වා දුන්නේ ඉතා වැදගත් වෙනස්වන අවස්ථාවක්, එනම් යමෙකු C # 5 හි කේත ලිවීම, එය පරීක්ෂා කිරීම, පසුව එය තවමත් C # 4 භාවිතා කරන පුද්ගලයින් සමඟ බෙදා ගැනීම, එය නිවැරදි යැයි බොළඳ ලෙස විශ්වාස කරන අයයි. එවැනි තත්වයක් හේතුවෙන් පීඩාවට පත් වූවන්ගේ සංඛ්‍යාව ඉතා කුඩා යැයි බලාපොරොත්තු වෙමු.
එරික් ලිපර්ට්

30
අනෙක් අතට, රීෂාර්පර් සෑම විටම මෙය අල්ලාගෙන එය "නවීකරණය කරන ලද වසා දැමීමේ ප්‍රවේශය" ලෙස වාර්තා කරයි. පසුව, Alt + Enter එබීමෙන්, එය ස්වයංක්‍රීයව ඔබ වෙනුවෙන් ඔබේ කේතය නිවැරදි කරයි. jetbrains.com/resharper
මයික් චේම්බර්ලින්

191

ඔබ ඉල්ලන දේ එරික් ලිපර්ට් සිය බ්ලොග් සටහනේ හොඳින් ආවරණය කරයි හානිකර යැයි සැලකෙන ලූප් විචල්‍යය සහ එහි අනුප්‍රාප්තිකය වසා දැමීම .

මට නම්, වඩාත් ඒත්තු ගැන්වෙන තර්කය නම්, එක් එක් පුනරාවර්තනයේ නව විචල්‍යයක් තිබීම for(;;)ස්ටයිල් ලූපයට නොගැලපෙන බවයි. int iඑක් එක් පුනරාවර්තනයේ දී නව එකක් ලබා ගැනීමට ඔබ අපේක්ෂා for (int i = 0; i < 10; i++)කරනවාද?

මෙම හැසිරීමේ වඩාත් පොදු ගැටළුව වන්නේ පුනරාවර්තන විචල්‍යයට වඩා වසා දැමීම වන අතර එයට පහසු ක්‍රියාමාර්ගයක් ඇත:

foreach (var s in strings)
{
    var s_for_closure = s;
    query = query.Where(i => i.Prop == s_for_closure); // access to modified closure

මෙම ගැටලුව පිළිබඳ මගේ බ්ලොග් සටහන: C # හි foreach විචල්‍යය වසා දැමීම .


18
අවසාන වශයෙන්, මෙය ලියන විට මිනිසුන්ට ඇත්ත වශයෙන්ම අවශ්‍ය වන්නේ බහු විචල්‍යයන් නොවේ, එය වටිනාකමට වඩා වසා දැමීමයි . පොදුවේ ගත් කල ඒ සඳහා භාවිතා කළ හැකි වාක්‍ය ඛණ්ඩයක් ගැන සිතීම දුෂ්කර ය.
සසම්භාවී 832

1
ඔව්, අගය අනුව වසා දැමිය නොහැක, කෙසේ වෙතත් ඉතා පහසු විසඳුමක් ඇත, ඇතුළත් කිරීමට මගේ පිළිතුර සංස්කරණය කර ඇත.
ක්‍රිස්

6
C # හි යොමු කිරීම් වලට වඩා එය ඉතා නරක වසා දැමීම් වේ. ඒවා පෙරනිමියෙන් අගයන් ඉක්මවා ගියහොත්, ඒ වෙනුවට විචල්යයන් මත වැසීම අපට පහසුවෙන් නියම කළ හැකිය ref.
ෂෝන් යූ

2
@ ක්‍රිස්, මෙය නොගැලපීමට වඩා බලහත්කාරයෙන් අනුකූලතාව හානිකර වන අවස්ථාවකි. එය මිනිසුන් අපේක්ෂා කරන ආකාරයටම “ක්‍රියා කළ යුතු” අතර, වෙනස් කළ වසා දැමීමේ ගැටලුවට (මා වැනි) ප්‍රවේශය ගැන දැන ගැනීමට පෙර ගැටළු වලට මුහුණ දුන් පුද්ගලයින් සංඛ්‍යාව අනුව, ලූප සඳහා ලූපයකට වඩා වෙනස්ව පුරෝකථනය භාවිතා කරන විට පැහැදිලිවම මිනිසුන් වෙනස් දෙයක් අපේක්ෂා කරයි. .
ඇන්ඩි

2
# Random832 C # ගැන නොදන්නා නමුත් පොදු LISP හි ඒ සඳහා වාක්‍ය ඛණ්ඩයක් ඇති අතර, එය විකෘති විචල්‍යයන් සහ වසා දැමීම් ඇති ඕනෑම භාෂාවක් (නැත, අනිවාර්යයෙන්ම ) තිබිය යුතු බව පෙන්නුම් කරයි. අපි එක්කෝ වෙනස්වන ස්ථානය ගැන සඳහන් කිරීම හෝ යම් නිශ්චිත මොහොතක එය සතු අගයක් (වසා දැමීමක් නිර්මාණය කිරීම). මෙය පයිතන් සහ යෝජනා ක්‍රමයේ සමාන දේවල් සාකච්ඡා කරයි ( cutrefs / vars cuteසඳහා සහ තක්සේරු කරන ලද අගයන් අර්ධ වශයෙන් තක්සේරු කරන ලද වසා දැමීම්වල තබා ගැනීම සඳහා ).
විල් නෙස්

104

මෙයින් සපා කා ඇති බැවින්, ඕනෑම වසා දැමීමකට මාරු කිරීම සඳහා මා භාවිතා කරන අභ්‍යන්තර විෂය පථයට දේශීයව අර්ථ දක්වා ඇති විචල්‍යයන් ඇතුළත් කිරීමේ පුරුද්දක් මට ඇත. ඔබේ උදාහරණයේ:

foreach (var s in strings)
    query = query.Where(i => i.Prop == s); // access to modified closure

මම කරනවා:

foreach (var s in strings)
{
    string search = s;
    query = query.Where(i => i.Prop == search); // New definition ensures unique per iteration.
}        

ඔබට එම පුරුද්ද ඇති වූ පසු, ඔබ සැබවින්ම බාහිර විෂය පථයන්ට බැඳීමට අදහස් කළ ඉතා දුර්ලභ අවස්ථාවකදී එය වළක්වා ගත හැකිය . ඇත්තම කිව්වොත් මම හිතන්නේ නැහැ මම කවදාවත් එහෙම කරලා නැහැ කියලා.


24
එය සාමාන්‍ය කාර්යසාධනයයි දායකත්වයට ස්තූතියි. මෙම රටාව හඳුනාගෙන එය ඔබේ අවධානයට යොමු කිරීමට තරම් රිෂාර්පර් දක්ෂයි, එය කදිමයි. මම ටික මෙම රටාව විසින් ටිකක් වී නැත, නමුත් එය එරික් Lippert වචන, "තනි වඩාත් පොදු වැරදි දෝෂ වාර්තාවක් අප ලබා," මම දැන ගැනීමට කුතුහලයක් ඇති විය තුළ, නිසා ඇයි වඩා වැඩි එය වළක්වා ගැනීමට ආකාරය .
StriplingWarrior

62

C # 5.0 හි, මෙම ගැටළුව නිරාකරණය කර ඇති අතර ඔබට ලූප විචල්‍යයන් වසා දමා ඔබ අපේක්ෂා කරන ප්‍රති results ල ලබා ගත හැකිය.

භාෂා පිරිවිතරයේ මෙසේ සඳහන් වේ:

8.8.4 පුරෝකථන ප්‍රකාශය

(...)

පෝරමයේ පුරෝකථන ප්‍රකාශයක්

foreach (V v in x) embedded-statement

ඉන්පසු පහත පරිදි පුළුල් වේ:

{
  E e = ((C)(x)).GetEnumerator();
  try {
      while (e.MoveNext()) {
          V v = (V)(T)e.Current;
          embedded-statement
      }
  }
  finally {
       // Dispose e
  }
}

(...)

vකාවැද්දූ ප්‍රකාශයේ සිදුවන ඕනෑම නිර්නාමික ශ්‍රිතයක් මගින් එය ග්‍රහණය කරගන්නේ කෙසේද යන්න සඳහා කාල ලූපය තුළ ස්ථානගත කිරීම වැදගත් වේ. උදාහරණයක් වශයෙන්:

int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
    if (f == null) f = () => Console.WriteLine("First value: " + value);
}
f();

නම් vවූ අතර, ලූප පිටත ප්රකාශයට පත් කරන ලදි, ඒ සියල්ල අනුකරණ අතර හවුල් වීම සහ පුඩුවක් සඳහා පසු එහි අගය අවසන් අගය, වනු ඇත 13පිලිබඳ යාදින්න දේ වන, fමුද්රණය ඇත. ඒ වෙනුවට, එක් එක් නැවතීමේ ක්‍රියාවලියට තමන්ගේම විචල්‍යයක් ඇති හෙයින්, පළමු පුනරාවර්තනයේදී vග්‍රහණය කරගත් fඅගය දිගටම අගය රඳවා තබා ගනී 7, එය මුද්‍රණය කරනු ලැබේ. ( සටහන: C # හි පෙර සංස්කරණ කාල ලූපයෙන් vපිටත ප්‍රකාශයට පත් කරන ලදි. )


1
C # හි මෙම මුල් අනුවාදය කාල ලූපය තුළ v ලෙස ප්‍රකාශ කළේ ඇයි? msdn.microsoft.com/en-GB/library/aa664754.aspx
colinfang

4
@colinfang එරික්ගේ පිළිතුර කියවීමට වග බලා ගන්න : C # 1.0 පිරිවිතර ( ඔබේ සබැඳියේ අපි කතා කරන්නේ VS 2003 ගැන, එනම් C # 1.2 ) ඇත්ත වශයෙන්ම ලූප් විචල්‍යය ලූප් ශරීරය තුළ හෝ පිටත තිබේද යන්න නොකියයි. . C # 2.0 හි සංවෘත අර්ථකථන හඳුන්වා දුන් විට, "for" පුඩුවට අනුකූලව ලූප විචල්‍යය ලූපයෙන් පිටත තැබීමට තෝරා ගන්නා ලදී.
පාවුලෝ මොරෙට්ටි

1
ඉතින් ඔබ කියන්නේ සබැඳියේ උදාහරණ එකල නිශ්චිත පිරිවිතර නොවන බවයි?
කොලින්ෆැං

4
olcolinfang ඒවා නිශ්චිත පිරිවිතරයන් විය. ගැටළුව වන්නේ අප කතා කරන්නේ පසුව හඳුන්වා දුන් අංගයක් (එනම් ශ්‍රිත වැසීම) (C # 2.0 සමඟ) ය. C # 2.0 පැමිණි විට ඔවුන් තීරණය කළේ ලූප් විචල්‍යය ලූපයෙන් පිටත තැබීමටයි. C # 5.0 සමඟ ඔවුන් නැවත මනස වෙනස් කිරීමට වඩා :)
Paolo Moretti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.