c# क्या फोरैच में चर के सी#के पुन: उपयोग का कोई कारण है?




foreach lambda (4)

सी # में लैम्ब्डा अभिव्यक्तियों या अज्ञात तरीकों का उपयोग करते समय, हमें संशोधित बंद करने के लिए पहुंच से सावधान रहना होगा। उदाहरण के लिए:

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

संशोधित बंद होने के कारण, उपर्युक्त कोड क्वेरी के सभी खंडों को अंतिम मूल्य पर आधारित होने का कारण बन जाएगा।

जैसा कि here बताया गया here , ऐसा इसलिए होता है क्योंकि उपरोक्त foreach लूप में घोषित s वैरिएबल को संकलक में इस तरह अनुवादित किया जाता है:

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

इस तरह के बजाय:

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

जैसा कि here बताया गया here , लूप के बाहर एक चर घोषित करने के लिए कोई प्रदर्शन लाभ नहीं हैं, और सामान्य परिस्थितियों में एकमात्र कारण यह है कि मैं ऐसा करने के बारे में सोच सकता हूं यदि आप लूप के दायरे के बाहर चर का उपयोग करने की योजना बना रहे हैं:

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 लूप के साथ कर सकते हैं इस तरह से कि अगर आप आंतरिक-स्कोप्ड वैरिएबल के साथ संकलित नहीं होते हैं, या यह सिर्फ एक मनमाना विकल्प है जो अज्ञात तरीकों से पहले बनाया गया था और लैम्ब्डा अभिव्यक्ति उपलब्ध या आम थी, और जो तब से संशोधित नहीं किया गया है?


आप जो पूछ रहे हैं वह एरिक लिपर्ट द्वारा अपने ब्लॉग पोस्ट क्लोजिंग ओवर द लूप वैरिएबल में पूरी तरह से कवर किया गया है जिसे हानिकारक और इसकी अगली कड़ी माना जाता है

मेरे लिए, सबसे भरोसेमंद तर्क यह है कि प्रत्येक पुनरावृत्ति में नया चर होने for(;;) स्टाइल लूप के साथ असंगत होगा। क्या आप प्रत्येक पुनरावृत्ति में 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

इस मुद्दे के बारे में मेरा ब्लॉग पोस्ट: सी # में फोरैच वैरिएबल पर बंद करें


इसके द्वारा काटकर, मेरे पास आंतरिक रूप से परिभाषित चर शामिल करने की आदत है जिसमें मैं किसी भी बंदरगाह में स्थानांतरित करने के लिए उपयोग करता हूं। आपके उदाहरण में:

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.

एक बार जब आप उस आदत को प्राप्त कर लेते हैं, तो आप उस दुर्लभ मामले में इससे बच सकते हैं जिसका आप वास्तव में बाहरी क्षेत्रों से जुड़ना चाहते हैं। ईमानदार होने के लिए, मुझे नहीं लगता कि मैंने कभी ऐसा किया है।


कंपाइलर वैरिएबल को इस तरह से घोषित करता है जो इसे किसी त्रुटि के लिए अत्यधिक प्रवण बनाता है जिसे अक्सर खोजने और डिबग करना मुश्किल होता है, जबकि कोई समझदार लाभ नहीं होता है।

आपकी आलोचना पूरी तरह से उचित है।

मैं इस समस्या पर विस्तार से चर्चा करता हूं:

लूप चर पर बंद हानिकारक माना जाता है

क्या ऐसा कुछ है जो आप फोरच लूप के साथ कर सकते हैं इस तरह से कि अगर आप आंतरिक-स्कोप्ड वैरिएबल से संकलित नहीं होते हैं तो आप नहीं कर सकते? या यह सिर्फ एक मनमाना विकल्प है जो अज्ञात तरीकों से पहले बनाया गया था और लैम्ब्डा अभिव्यक्ति उपलब्ध थी या आम थी, और तब से संशोधित नहीं किया गया है?

बाद वाला। सी # 1.0 विनिर्देश वास्तव में यह नहीं कहता था कि लूप चर लूप बॉडी के अंदर या बाहर था, क्योंकि इसमें कोई अवलोकन करने योग्य अंतर नहीं था। जब सी # 2.0 में क्लोजर सेमेन्टिक्स पेश किए गए थे, तो लूप के बाहर लूप वैरिएबल को "फॉर" लूप के साथ रखने के लिए पसंद किया गया था।

मुझे लगता है कि यह कहना उचित है कि सभी उस फैसले पर अफसोस करते हैं। यह सी # में सबसे खराब "गॉथचास" में से एक है, और हम इसे ठीक करने के लिए ब्रेकिंग चेंज लेने जा रहे हैं। सी # 5 में फोरच लूप वैरिएबल लॉप के शरीर के अंदर तार्किक रूप से होगा, और इसलिए बंद होने पर हर बार ताज़ा प्रतिलिपि मिल जाएगी।

लूप के लिए नहीं बदला जाएगा, और परिवर्तन सी # के पिछले संस्करणों में "पिछला पोर्ट" नहीं होगा। इसलिए इस मुहावरे का उपयोग करते समय आपको सावधान रहना चाहिए।


सी # 5.0 में, यह समस्या ठीक हो गई है और आप लूप वैरिएबल को बंद कर सकते हैं और परिणाम प्राप्त कर सकते हैं।

भाषा विनिर्देश कहता है:

8.8.4 प्रस्ताव बयान

(...)

फॉर्म का एक foreach स्टेटमेंट

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 का आविष्कार प्रिंट करेगा। इसके बजाए, क्योंकि प्रत्येक पुनरावृत्ति के अपने चर वैरिएबल होते हैं, पहले पुनरावृत्ति में f द्वारा कब्जा कर लिया गया एक मूल्य 7 जारी रखेगा, जो मुद्रित किया जाएगा। ( नोट: सी # घोषित v पुराने संस्करण जबकि लूप के बाहर। )





anonymous-methods