2009-11-02 4 views
7

में प्रतिनिधियों के साथ समस्या निम्न प्रोग्राम में, DummyMethod हमेशा प्रिंट 5। लेकिन अगर हम इसके बजाय टिप्पणी कोड का उपयोग करते हैं, तो हमें अलग-अलग मान मिलते हैं (यानी 1, 2, 3, 4)। क्या कोई यह बता सकता है कि यह क्यों खुश है?सी #

 delegate int Methodx(object obj); 

     static int DummyMethod(int i) 
     { 
      Console.WriteLine("In DummyMethod method i = " + i); 
      return i + 10; 
     } 

     static void Main(string[] args) 
     {  
      List<Methodx> methods = new List<Methodx>(); 

      for (int i = 0; i < 5; ++i) 
      { 
       methods.Add(delegate(object obj) { return DummyMethod(i); }); 
      } 

      //methods.Add(delegate(object obj) { return DummyMethod(1); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(2); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(3); }); 
      //methods.Add(delegate(object obj) { return DummyMethod(4); }); 

      foreach (var method in methods) 
      { 
       int c = method(null); 
       Console.WriteLine("In main method c = " + c); 
      } 
     } 

यदि निम्न कोड का उपयोग किया जाता है, तो मुझे वांछित परिणाम मिलते हैं।

 for (int i = 0; i < 5; ++i) 
     { 
      int j = i; 
      methods.Add(delegate(object obj) { return DummyMethod(j); }); 
     } 
+2

इस प्रश्न का समर्थन। इस प्रकार का व्यवहार सिर्फ "खतरनाक" लगता है। मैं शर्त लगाता हूं कि यह उन कठिन-से-डीबग मुद्दों में से एक के रूप में अधिक बार आने वाला है क्योंकि सी # encapsulating व्यवहार के प्रतिनिधि-उन्मुख तरीके की ओर अधिक से अधिक चलता है। यह स्विच क्लॉज के लूपिंग समकक्ष की तरह है जो अनिवार्य "ब्रेक" के बिना नीचे की ओर कैस्केड करने की अनुमति है खंड –

+0

@ नील: मैं मानता हूं कि यह गॉचा-हॉटस्पॉट है। जब यह "foreach" के साथ देखा जाता है तो यह अधिक भ्रमित होता है - इतना है कि सी # टीम उस मामले के व्यवहार को बदलने पर विचार कर रही है। (एक परिदृश्य के बारे में सोचना मुश्किल है जहां foreach व्यवहार वांछनीय है; लूप स्थिति अधिक समझने योग्य है क्योंकि यह स्पष्ट है कि चर केवल एक बार घोषित किया जाता है।) –

+0

[सी # कैप्चर किए गए चरणीय लूप] के संभावित डुप्लिकेट (http: //stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop) – nawfal

उत्तर

17
समस्या

है कि आप हर प्रतिनिधि में एक ही चर i पर कब्जा कर रहे हैं - जो पाश के अंत तक सिर्फ इसके बजाय मूल्य 5.

है, तो आपको प्रत्येक प्रतिनिधि एक अलग कैप्चर करना चाहते हैं चर, जो पाश में एक नया वेरिएबल घोषित करने का मतलब है:

for (int i = 0; i < 5; ++i) 
{ 
    int localCopy = i; 
    methods.Add(delegate(object obj) { return DummyMethod(localCopy); }); 
} 

यह एक बहुत आम "पकड़ लिया" - आप पर कब्जा कर लिया चर और my closures article में बंद के बारे में थोड़ा और अधिक पढ़ सकते हैं।

+1

जॉन, ऐसा क्यों होता है मेरे सिर को पाने की कोशिश कर रहा है। मैंने "i" को मूल्य-प्रकार की सोच के रूप में देखा "यह एक प्रतिलिपि के रूप में पारित किया जाना चाहिए", इसलिए मैं नहीं देख पा रहा था कि यह संदर्भ-जैसा व्यवहार कैसे था ... आपको सारांश/एक-लाइनर मिला मुझे सही दिशा में इंगित करें? –

+2

अंतिम लिंक पर एक नज़र डालें। अपने सिर के दौर को पाने की बात यह है कि यह वैरिएबल का * वैल्यू * नहीं है जिसे कैप्चर किया गया है - यह वैरिएबल ही है। –

+1

इस बारे में सोचें: ऐसा लगता है कि इस तरह की एक छोटी सी इनलाइन घटना है जिसे विधि में उस स्थान पर सही कहा जाता है और इस प्रकार सभी चर और राज्य तक पहुंच होती है, केवल जब आप विधि नहीं बनाई जाती हैं तो वे चर और स्थिति प्राप्त करते हैं। इस प्रकार जब आप अज्ञात विधि बनाते हैं तो विधि को तब नहीं कहा जाता है जब आप विधि को एक्सेस करते हैं। – RCIX

2

मुझे लगता है कि ऐसा इसलिए है क्योंकि चर i ढेर करने के लिए रखा जाता है (यह एक चर पर कब्जा कर लिया है)

this answer पर एक नज़र डालें।

+1

मुझे लगता है कि "कैप्चर वैरिएबल" अधिक उपयोगी शब्दावली है - ठीक है क्योंकि यह * वैरिएबल * है जिसे इसके * वैल्यू * के बजाय कैप्चर किया जाता है। –

+0

@ जोन: उचित लगता है, मैं इसे ठीक करता हूं –

4

आप (परावर्तक का प्रयोग करके) आप अंतर देख सकते हैं उत्पन्न कोड को देखें, तो:

private static void Method2() 
{ 
    List<Methodx> list = new List<Methodx>(); 
    Methodx item = null; 
    <>c__DisplayClassa classa = new <>c__DisplayClassa(); 
    classa.i = 0; 
    while (classa.i < 5) 
    { 
     if (item == null) 
     { 
      item = new Methodx(classa.<Method2>b__8); 
     } 
     list.Add(item); 
     classa.i++; 
    } 
    foreach (Methodx methodx2 in list) 
    { 
     Console.WriteLine("In main method c = " + methodx2(null)); 
    } 
} 

आप प्रारंभिक कोड यह पृष्ठभूमि में एक अस्थायी वर्ग बनाता उपयोग करते हैं, इस वर्ग के लिए एक संदर्भ रखती है "मैं" चर, तो जॉन के जवाब के अनुसार, आप केवल इसके अंतिम मूल्य को देखते हैं।

private sealed class <>c__DisplayClassa 
{ 
    // Fields 
    public int i; 

    // Methods 
    public <>c__DisplayClassa(); 
    public int <Method2>b__8(object obj); 
} 

मैं वास्तव में Reflector में कोड देख देखने के लिए क्या हो रहा है की सलाह देते हैं, इसकी मैं कैसे कब्जा कर लिया चर की भावना बनाया है। सुनिश्चित करें कि आपने विकल्प मेनू में कोड का अनुकूलन ".NET 1.0" पर सेट किया है, अन्यथा यह सभी पिछड़े दृश्यों को छिपाएगा।