2012-05-24 8 views
6

मैंने हाल ही में कॉलिंग ईवेंट को आसान बनाने के लिए सी # एक्सटेंशन विधियों का उपयोग करने के बारे में सीखा और मैं उन्हें अधिक से अधिक उपयोग कर रहा हूं। मैंने हाल ही में एक अजीब मुद्दा मारा है जिसे मैं समझ में नहीं आता, और मैं सोच रहा था कि कोई इसे समझा सकता है या नहीं।सी # एक्सटेंशन विधियों के साथ घटनाओं को संभालना

समस्या तब होती है जब कोई ईवेंट ईवेंट ईवेंट को किसी अन्य ईवेंट के ईवेंट हैंडलर के रूप में सेट करने का प्रयास किया जाता है।

public static class EventHandlerExtensions 
{ 
    public static void Raise<TEventArgs>(
     this EventHandler<TEventArgs> eventHandler, 
     object sender, TEventArgs args) where TEventArgs:EventArgs 
    { 
     if (eventHandler != null) 
     { 
      eventHandler(sender, args); 
     } 
    } 
} 

public class Test 
{ 
    private event EventHandler<EventArgs> EventA; 
    private event EventHandler<EventArgs> EventB; 

    public Test() 
    { 
     Console.WriteLine("::Start"); 
     EventB += EventA.Raise; 
     EventA += (s, a) => Console.WriteLine("Event A raised"); 
     EventB.Raise(this, EventArgs.Empty); 
     Console.WriteLine("::End"); 
    } 
} 

इस उदाहरण में, EventA की EventB खोज किए जाने पर एक परिणाम के रूप शुरू हो जाना चाहिए: यहाँ मैं क्या कर रहा का एक उदाहरण है। हालांकि, जब मैं इस कोड को चलाता हूं, इवेंटबी आग लगती है, लेकिन ए पर एक्सटेंशन विधि को इसके लिए कोई श्रोताओं नहीं मिलते हैं। इसके अलावा

Console.WriteLine("::Start"); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB += EventA.Raise; 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

, एक लैम्ब्डा से EventA.Raise बुला ठीक काम करता है:

अगर मैं क्रम में परिवर्तन किया है, सब कुछ ठीक काम करता है

Console.WriteLine("::Start"); 
EventB += (s, a) => EventA.Raise(s, a); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

यह सिर्फ एक सरल उदाहरण है, लेकिन मैं मैं एक वर्ग बनाने की कोशिश कर रहा हूं जो संभवतः सबसे आसान तरीके से जोड़े गए ईवेंट स्रोतों की घटनाओं को फिर से प्रेषित कर सकता है। मैं सिर्फ एक ही घटनाओं को फिर से खोलने के लिए नामित विधियों को नहीं बनाना चाहता हूं, और मैं बाद में लैम्ब्डा कार्यों की सूचियों को संग्रहित नहीं करना चाहता हूं जिन्हें मैं बाद में ईवेंट हैंडलर से अनदेखा कर सकता हूं। ज्यादातर, मैं सिर्फ उत्सुक हूं कि यह क्यों हो रहा है?

कोई विचार?

+0

यदि आपके पास सब्सक्राइब करने के लिए बहुत से ईवेंट हैंडलर हैं तो यह वास्तव में खराब हो सकता है। अच्छा सवाल, हालांकि !!! :) – IAbstract

+0

जब मैं VS2010 में अपना कोड लिखता हूं, तो 'कंसोल। राइटलाइन ("स्टार्ट") के बाद' EventA.R' टाइप करना; 'इंटेलिजिसेंस में एक्सटेंशन विधि नहीं दिखाता है। यह करता है अगर मैं इसे 'कंसोल' लिखने से पहले टाइप करता हूं। राइटलाइन ("एंड"); '। अजीब। – Guillaume

उत्तर

4

आप अपने Raise फ़ंक्शन द्वारा बंद होने पर EventA का पुराना मान कैप्चर करते हैं। बाद में आप + = यह EventA के मान को बदलते हैं, लेकिन आपके बंद होने का अभी भी पुराना मान है।

आप कोड:

var oldEventA = EventA; 
EventB += oldEventA.Raise; // captures old value here 
// now EventA changed to new value 
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");) 

आप वास्तव में उठता है कि कोड को सत्यापित करने के EventB += EventA.Raise से पहले करने के लिए निम्न जोड़ सकते हैं:

EventB += EventA.Raise; 
EventA += (s, a) => Console.WriteLine("Event A raised"); 

बराबर कोड है जो यह स्पष्ट करता है कि क्यों आप पुराने प्रतिनिधि पाने में विस्तार किया जा सकता है ए के लिए पुरानी घटना:

EventA += (s, a) => Console.WriteLine("Old Event A raised"); 
2

प्रतिनिधि वस्तुएं अपरिवर्तनीय हैं। तारों की तरह बहुत। तो जब आप EventA असाइन करते हैं, तो आप नया ऑब्जेक्ट बनाते हैं। इवेंट बी अभी भी पुराने को लक्षित कर रहा है, जिसकी अभी तक कोई इवेंट हैंडलर असाइन नहीं किया गया है। समस्या को ठीक करने के लिए आपको दो कथनों को स्वैप करना होगा।