संशोधित बंद करने के लिए प्रवेश (2)


101

यह एक्सेस से संशोधित क्लोजर से प्रश्न का विस्तार है । मैं सिर्फ यह सत्यापित करना चाहता हूं कि उत्पादन के उपयोग के लिए निम्नलिखित वास्तव में पर्याप्त सुरक्षित है या नहीं।

List<string> lists = new List<string>();
//Code to retrieve lists from DB    
foreach (string list in lists)
{
    Button btn = new Button();
    btn.Click += new EventHandler(delegate { MessageBox.Show(list); });
}

मैं केवल एक बार प्रति स्टार्टअप के माध्यम से चलता हूं। अभी के लिए यह ठीक काम करने लगता है। जैसा कि जॉन ने किसी मामले में प्रतिवाद परिणाम के बारे में उल्लेख किया है। तो मुझे यहां क्या देखना होगा? यदि सूची को एक से अधिक बार चलाया जाए तो क्या यह ठीक होगा?


18
बधाई हो, अब आप Resharper प्रलेखन का हिस्सा हैं। confluence.jetbrains.net/display/ReSharper/…
कोंग्रेस

1
यह एक मुश्किल था लेकिन उपर्युक्त स्पष्टीकरण ने मेरे लिए यह स्पष्ट कर दिया: यह सही प्रतीत हो सकता है लेकिन, वास्तविक तथ्य में, जब भी कोई बटन क्लिक किया जाता है, तब str चर के केवल अंतिम मूल्य का उपयोग किया जाएगा। इसका कारण यह है कि foreach थोड़ी देर के लूप में अनियंत्रित हो जाता है, लेकिन पुनरावृति चर को इस लूप के बाहर परिभाषित किया जाता है। इसका मतलब यह है कि जब तक आप संदेश बॉक्स दिखाते हैं, तब तक str का मान स्ट्रिंग्स के संग्रह में पहले से ही अंतिम मान पर आधारित हो सकता है।
डैनियल

जवाबों:


159

C # 5 से पहले, आपको फ़ॉरच के अंदर एक चर को फिर से घोषित करने की आवश्यकता है - अन्यथा यह साझा किया गया है, और आपके सभी हैंडलर अंतिम स्ट्रिंग का उपयोग करेंगे:

foreach (string list in lists)
{
    string tmp = list;
    Button btn = new Button();
    btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}

गौरतलब है कि C # 5 के बाद से, यह बदल गया है, और विशेष रूप से के मामले मेंforeach , आपको इसे और अधिक करने की आवश्यकता नहीं है: प्रश्न में कोड अपेक्षा के अनुरूप काम करेगा।

इस परिवर्तन के बिना काम न करने के लिए, निम्नलिखित पर विचार करें:

string[] names = { "Fred", "Barney", "Betty", "Wilma" };
using (Form form = new Form())
{
    foreach (string name in names)
    {
        Button btn = new Button();
        btn.Text = name;
        btn.Click += delegate
        {
            MessageBox.Show(form, name);
        };
        btn.Dock = DockStyle.Top;
        form.Controls.Add(btn);
    }
    Application.Run(form);
}

C # 5 से पहले ऊपर चलाएं , और यद्यपि प्रत्येक बटन एक अलग नाम दिखाता है, बटन पर क्लिक करने से "विल्मा" चार बार दिखाई देती है।

इसका कारण यह है कि भाषा की युक्ति (ECMA 334 v4, 15.8.4) (C # 5 से पहले) परिभाषित होती है:

foreach (V v in x) embedded-statement उसके बाद इसका विस्तार किया जाता है:

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

ध्यान दें कि लूप के बाहर चर v(जो आपका है list) घोषित किया गया है। इसलिए कैप्चर किए गए चर के नियमों द्वारा, सूची के सभी पुनरावृत्तियों पर कब्जा किए गए चर धारक को साझा करेंगे।

C # 5 के बाद से, इसे बदल दिया जाता है: लूप के अंदर पुनरावृत्ति चर ( v) स्कूप किया जाता है। मेरे पास एक विनिर्देश संदर्भ नहीं है, लेकिन यह मूल रूप से बन जाता है:

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

फिर से खोलना; यदि आप सक्रिय रूप से एक अनाम हैंडलर को अनसब्सक्राइब करना चाहते हैं, तो चाल को हैंडलर को स्वयं पकड़ना है:

EventHandler foo = delegate {...code...};
obj.SomeEvent += foo;
...
obj.SomeEvent -= foo;

इसी तरह, यदि आप एक बार केवल ईवेंट-हैंडलर चाहते हैं (जैसे लोड आदि):

EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
  // ... code
  obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;

यह अब स्व-सदस्यता रद्द करना है;


अगर ऐसा है, तो अस्थायी चर स्मृति में रहेगा, जब तक कि ऐप बंद नहीं हो जाता है, जैसा कि प्रतिनिधि को सेवा देना है, और यह करने के लिए सलाह नहीं दी जाएगी कि बहुत बड़ी छोरों के लिए यदि चर बहुत अधिक मेमोरी लेता है। क्या मैं सही हू?
दोषपूर्ण

1
यह लंबे समय तक स्मृति में रहेगा कि घटना के साथ चीजें (बटन) हैं। अन-सबस्क्राइबिंग का एक तरीका है केवल-एक प्रतिनिधि, जिसे मैं पोस्ट में जोड़ूंगा।
मार्क Gravell

2
लेकिन अपनी बात पर योग्य होने के लिए: हाँ, कैप्चर किए गए चर वास्तव में एक चर के दायरे को बढ़ा सकते हैं। आप चीजें आप की अपेक्षा नहीं थी ... पर कब्जा करने के लिए नहीं सावधान रहना चाहिए
मार्क Gravell

1
क्या आप C # 5.0 युक्ति में परिवर्तन के संबंध में अपना जवाब अपडेट कर सकते हैं? बस इसे C # में foreach loops के बारे में एक बेहतरीन विकी डॉक्यूमेंटेशन बनाने के लिए। C # 5.0 संकलक में परिवर्तन के संबंध में पहले से ही कुछ अच्छे उत्तर दिए गए हैं जो foreach loops bit.ly/WzBV3L का उपचार कर रहे हैं, लेकिन वे विकी जैसे संसाधन नहीं हैं।
इल्या इवानोव

1
@ हाँ, for5.0 में अपरिवर्तित है
मार्क ग्रेवल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.