2008-10-17 12 views
38

मैं सी #, .NET 3.5 का उपयोग कर रहा हूं। मैं समझता हूँ कि कैसे घटनाओं का उपयोग करने, कैसे मेरी कक्षा में उन्हें घोषित करने के लिए, उन्हें कैसे कहीं और, आदि से एक काल्पनिक उदाहरण हुक करने:सी # घटनाक्रम दृश्यों के पीछे कैसे काम करते हैं?

public class MyList 
{ 
    private List<string> m_Strings = new List<string>(); 
    public EventHandler<EventArgs> ElementAddedEvent; 

    public void Add(string value) 
    { 
     m_Strings.Add(value); 
     if (ElementAddedEvent != null) 
      ElementAddedEvent(value, EventArgs.Empty); 
    } 
} 

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     MyList tmp = new MyList(); 
     tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired); 
     tmp.Add("test"); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

हालांकि, मैं क्या कर समझ में नहीं आता, तब होता है जब एक वाणी एक इवेंट हैंडलर

public EventHandler<EventArgs> ElementAddedEvent; 

यह कभी शुरू नहीं हुआ है - तो क्या, वास्तव में, ElementAddedEvent है? यह क्या इंगित करता है? निम्नलिखित काम करेंगे नहीं, क्योंकि eventhandler प्रारंभ कभी नहीं किया गया है:

[TestClass] 
public class TestMyList 
{ 
    private bool m_Fired = false; 

    [TestMethod] 
    public void TestEvents() 
    { 
     EventHandler<EventArgs> somethingHappend; 
     somethingHappend += new EventHandler<EventArgs>(Fired); 
     somethingHappend(this, EventArgs.Empty); 
     Assert.IsTrue(m_Fired); 
    } 

    private void Fired(object sender, EventArgs args) 
    { 
     m_Fired = true; 
    } 
} 

मैं नोटिस एक EventHandler.CreateDelegate (...) है कि वहाँ है, लेकिन सभी विधि हस्ताक्षर सुझाव है कि यह केवल करने के लिए प्रतिनिधियों संलग्न के लिए प्रयोग किया जाता है ठेठ ElementAddedEvent + = नया EventHandler (MyMethod) के माध्यम से पहले से मौजूद EventHandler।

मुझे यकीन है कि नहीं कर रहा हूँ अगर क्या मैं मदद मिलेगी करने के लिए कोशिश कर रहा हूँ ... लेकिन अंत में मैं जिसका बच्चों LINQ में एक सार माता पिता DataContext रजिस्टर कर सकते हैं जो तालिका प्रकार वे चाहते हैं "मनाया के साथ आने के लिए करना चाहते हैं "इसलिए मेरे पास पहले अपडेट और आफ्टर अपडेट जैसी घटनाएं हो सकती हैं, लेकिन विशिष्ट प्रकार के लिए। कुछ इस तरह: - और मैं समझने के लिए :)

+0

मेरा जवाब भी देखें http://stackoverflow.com/questions/2598170/question-regarding-to-value-reference-type-of-events/3854421#3854421 –

उत्तर

64

मैं में इस लिखा है चाहते हैं

public class BaseDataContext : DataContext 
{ 
    private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>(); 

    public static void Observe(Type type) 
    { 
     if (m_ObservedTypes.ContainsKey(type) == false) 
     { 
      m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>()); 

      EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler); 

      eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; 
      m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler); 
     } 
    } 

    public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events 
    { 
     get { return m_ObservedTypes; } 
    } 
} 


public class MyClass 
{ 
    public MyClass() 
    { 
     BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate); 
    } 

    public void OnUserUpdated(object sender, EventArgs args) 
    { 
     // do something 
    } 
} 

इस बारे में सोचते हुए मुझे एहसास मैं सच में समझ में नहीं आता क्या घटनाओं के साथ विभागाध्यक्ष के तहत हो रहा है बनाया an article में विस्तार भी पर्याप्त मात्रा में है, लेकिन यहां सारांश है, यह मानते हुए आप delegates साथ काफी खुश हैं खुद को:

  • एक घटना है सिर्फ एक "जोड़ें" विधि और "निकालें" विधि, उसी तरह से है कि एक संपत्ति वास्तव में सिर्फ एक "प्राप्त करें" विधि और एक "सेट" मेथ है आयुध डिपो। (वास्तव में, सीएलआई एक "raise/आग" विधि भी देता है, लेकिन सी # कभी इसे उत्पन्न नहीं करता है।) मेटाडाटा घटनाओं के संदर्भों के साथ घटना का वर्णन करता है।
  • जब आप की घोषणा एक field-like event (अपने ElementAddedEvent की तरह) संकलक तरीकों और एक निजी क्षेत्र (प्रतिनिधि के रूप में एक ही प्रकार के) उत्पन्न करता है। कक्षा के भीतर, जब आप ElementAddedEvent का संदर्भ लेते हैं तो आप फ़ील्ड का जिक्र कर रहे हैं। कक्षा के बाहर, आप क्षेत्र का जिक्र कर रहे हैं।
  • जब कोई किसी ईवेंट (+ = ऑपरेटर के साथ) की सदस्यता लेता है जो ऐड विधि कहता है। जब वे सदस्यता समाप्त करते हैं (- = ऑपरेटर के साथ) जो हटा देता है।
  • फ़ील्ड जैसी घटनाओं के लिए, कुछ सिंक्रनाइज़ेशन है लेकिन अन्यथा जोड़ें/निकालें केवल प्रतिनिधि को कॉल करें। स्वत: जेनरेट किए गए फ़ील्ड के मान को बदलने के लिए Combine/Remove। इन दोनों परिचालनों में बैकिंग फील्ड को असाइन किया गया है - याद रखें कि प्रतिनिधि अपरिवर्तनीय हैं। दूसरे शब्दों में, स्वत: जनरेट कोड इस तरह बहुत ज्यादा है:

    // Backing field 
    // The underscores just make it simpler to see what's going on here. 
    // In the rest of your source code for this class, if you refer to 
    // ElementAddedEvent, you're really referring to this field. 
    private EventHandler<EventArgs> __ElementAddedEvent; 
    
    // Actual event 
    public EventHandler<EventArgs> ElementAddedEvent 
    { 
        add 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent += value; 
          __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); 
         } 
        } 
        remove 
        { 
         lock(this) 
         { 
          // Equivalent to __ElementAddedEvent -= value; 
          __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); 
         } 
        } 
    } 
    
  • अपने मामले में उत्पन्न क्षेत्र का प्रारंभिक मूल्य null है - और यह हमेशा null फिर से बन जाएगा, तो सभी ग्राहकों निकाल दिए जाते हैं, के रूप में वह यह है कि प्रतिनिधि का व्यवहार। हटाओ।

  • आप एक "नहीं-op" हैंडलर, आपके घटना की सदस्यता के लिए इतनी के रूप में तुच्छता की जांच से बचना चाहते हैं, तो आप कर सकते हैं:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {}; 
    

    delegate {} सिर्फ एक गुमनाम विधि है जो नहीं करता है 'है इसके पैरामीटर के बारे में परवाह नहीं है और कुछ भी नहीं करता है।

यदि कुछ भी अभी भी अस्पष्ट है, तो कृपया पूछें और मैं मदद करने की कोशिश करूंगा!

+0

क्या अच्छा, संक्षिप्त स्पष्टीकरण! – DOK

+2

@ dok1: मैंने घटनाओं को कुछ बार समझाया है :) यह सी #, आईएमओ के सबसे बुरे समझा क्षेत्रों में से एक है। –

+0

यह सुनिश्चित करने के लिए कि मैं सही ढंग से समझता हूं - एक फ़ील्ड जैसी घटना वास्तव में प्रतिनिधि द्वारा समर्थित होती है। अगर किसी के पास + = नहीं है, तो यह शून्य है (यही कारण है कि हमें शून्य की जांच करनी है)। पहला + = उस पर एक असाइनमेंट करता है, और दूसरा + = एक संयोजन करता है? – Matt

-3

हुड के तहत, घटनाएं केवल विशेष कॉलिंग सम्मेलनों के साथ प्रतिनिधि हैं। (। उदाहरण के लिए, आप एक घटना ऊपर उठाने से पहले तुच्छता के लिए जाँच करने के लिए नहीं है)

स्यूडोकोड में, Event.Invoke() इस तरह टूट जाती है:

घटना श्रोता कॉल प्रत्येक श्रोता तुल्यकालिक पर है, तो मनमाना क्रम में यह धागा।

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

याद रखने के लिए एक बड़ी चेतावनी यह है कि इवेंट हैंडलर उसी थ्रेड में निष्पादित होते हैं जैसे ईवेंट बढ़ता है। यह एक सामान्य थ्रेड के रूप में सोचने के लिए एक सामान्य मानसिक त्रुटि है। वे नहीं करते।

+8

जब तक आप सावधान न हों, तब तक आपको * ईवेंट * बढ़ाने से पहले शून्यता की जांच करनी होगी। घटनाएं वास्तव में संपत्तियों के क्षेत्र से अधिक प्रतिनिधि नहीं हैं। घटनाएं बस "सब्सक्राइब/सदस्यता समाप्त करें" के व्यवहार को समाहित करती हैं। –

+3

मुझे संदेह है कि कॉलिंग ऑर्डर मनमाने ढंग से है। मुझे विश्वास है कि यह फीफो है – Odys

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