2013-05-28 8 views
11

निम्नलिखित कोड 012 के बजाय 33 आउटपुट करता है। मुझे समझ में नहीं आता कि एक नया चर लूपस्कोपेडी एक ही चर को कैप्चर करने के बजाय प्रत्येक पुनरावृत्ति में क्यों नहीं पकड़ा जाता है।सी # लैम्ब्डा अभिव्यक्तियों के अंदर परिवर्तनीय घोषित

Action[] actions = new Action[3]; 

for (int i = 0; i < 3; i++) 
{ 

    actions [i] =() => {int loopScopedi = i; Console.Write (loopScopedi);}; 
} 

foreach (Action a in actions) a();  // 333 

होपवेवर, यह कोड 012 उत्पन्न करता है। दोनों के बीच क्या अंतर है?

Action[] actions = new Action[3]; 

for (int i = 0; i < 3; i++) 
{ 
    int loopScopedi = i; 
    actions [i] =() => Console.Write (loopScopedi); 
} 

foreach (Action a in actions) a();  // 012 
+1

आह यह एक संशोधित बंद करने का मुद्दा है - देखें http://stackoverflow.com/questions/235455/access-to-modified-closure –

+1

क्लोजर समस्या: http://www.codethinked.com/c-closures-explained उदाहरण के लिए। व्यवहार 0net= 4.5 –

+2

में अलग है एरिक लिपर्ट http://blogs.msdn.com/b/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part से इन 2 प्रविष्टियों को पढ़ें -two.aspx और http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-Considered-harmful.aspx – Icarus

उत्तर

7

यह "एक संशोधित बंद करने के लिए उपयोग" कहा जाता है मिलता है। असल में, केवल एक i चर है, और सभी तीन भेड़िये इसका जिक्र कर रहे हैं। अंत में, i वैरिएबल को तक बढ़ा दिया गया है, इसलिए सभी तीन कार्य प्रिंट करते हैं। (ध्यान दें कि लैम्ब्डा में int loopScopedi = i केवल चलाता है एक बार तुम लैम्ब्डा बाद में कहते हैं।)

दूसरे संस्करण में, आप हर यात्रा के लिए एक नया int loopScopedi बना रहे हैं तथा i के वर्तमान मूल्य है, जो 0 और है करने के लिए स्थापित प्रत्येक पुनरावृत्ति के लिए 1 और 2

आप lambdas इनलाइनिंग अधिक स्पष्ट रूप से देखने के लिए क्या हो रहा है की कल्पना करने की कोशिश कर सकते हैं:

foreach (Action a in actions) 
{ 
    int loopScopedi = i; // i == 3, since this is after the for loop 
    Console.Write(loopScopedi); // always outputs 3 
} 

बनाम:

foreach (Action a in actions) 
{ 
    // normally you could not refer to loopScopedi here, but the lambda lets you 
    // you have "captured" a reference to the loopScopedi variables in the lambda 
    // there are three loopScopedis that each saved a different value of i 
    // at the time that it was allocated 
    Console.Write(loopScopedi); // outputs 0, 1, 2 
} 
2

दोनों के बीच क्या अंतर है?

विभिन्न दायरे।

अपने पहले लूप में आप i वैरिएबल का जिक्र कर रहे हैं जिसे for लूप स्टेटमेंट स्कोप में परिभाषित किया गया है जबकि दूसरी लूप में आप स्थानीय चर का उपयोग कर रहे हैं। 333 आउटपुट इस तथ्य के परिणामस्वरूप है कि आपका पहला पाश 3 बार पुनरावृत्त करता है और तदनुसार i वैरिएबल 3 को बढ़ाया जाता है, फिर जब आप क्रियाओं को कॉल करते हैं, तो वे सभी समान परिवर्तनीय (i) का संदर्भ लेते हैं।

दूसरा पाश में आप प्रत्येकAction के लिए एक नया चर उपयोग कर रहे हैं तो आप 012.

2

चर एक लैम्ब्डा में कब्जा एक वर्ग लैम्ब्डा के बीच साझा में फहराया जाता है और बाहरी कोड

आपके पहले उदाहरण में, i एक बार फहराया जा रहा है और for() और सभी पास किए गए लैम्बडा दोनों के साथ उपयोग किया जा रहा है। जब तक आप Console.WriteLine तक पहुंचते हैं, ifor() लूप से तक पहुंच गया है।

आपके दूसरे उदाहरण में, लूप के प्रत्येक भाग के लिए एक नया loopScopedi फहराया जा रहा है, इसलिए इसे बाद के लूप से अप्रभावित छोड़ दिया गया है।

2

यह इस बारे में है कि सी # कैसे बंद हो जाता है। पहले उदाहरण में बंदरगाह सही ढंग से कब्जा नहीं किया जाएगा और आप हमेशा अंतिम मूल्य का उपयोग कर समाप्त हो जाएगा; लेकिन दूसरे उदाहरण में आप प्लेसहोल्डर में लूप वैरिएबल के वर्तमान मान को कैप्चर कर रहे हैं और फिर आप उस प्लेसहोल्डर का उपयोग करते हैं; जो सही समाधान प्रदान करता है।

और सी # 5.0 और पिछले संस्करणों में लूप के लिए सी # 5.0 और पिछले संस्करणों में लूप के लिए सी # कैप्चर करता है - एक ब्रेकिंग चेंज के बीच अंतर है।

मेरे पास (लगभग) एक ही प्रश्न था और मैंने इसके बारे में here सीखा।

संबंधित मुद्दे