2009-08-07 7 views
30

मुझे यकीन है कि मैं सिर्फ घटनाओं और/या सी # में प्रतिनिधियों के बारे में कुछ बुनियादी समझ नहीं कर रहा हूँ, लेकिन क्यों मैं इस कोड नमूने में बूलियन परीक्षण नहीं कर सकता:सी # में, मैं परीक्षण क्यों नहीं कर सकता अगर कोई ईवेंट हैंडलर कक्षा के बाहर कहीं भी शून्य हो गया है जिसे परिभाषित किया गया है?

public class UseSomeEventBase { 
    public delegate void SomeEventHandler(object sender, EventArgs e); 
    public event SomeEventHandler SomeEvent; 
    protected void OnSomeEvent(EventArgs e) { 
     // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
     if (SomeEvent != null) SomeEvent(this, e); 
    } 
} 

public class UseSomeEvent : UseSomeEventBase { 
    public bool IsSomeEventHandlerNull() { 
     // "LEFT HAND SIDE" COMPILER ERROR 
     return SomeEvent == null; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     var useSomeEvent = new UseSomeEvent(); 
     useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEvent.SomeEvent == null) { 

     } 
     var useSomeEventBase = new UseSomeEventBase(); 
     useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEventBase.SomeEvent == null) { 

     } 
    } 

    static void FuncToHandle(object sender, EventArgs e) { } 
} 

उत्तर

55

एक घटना वास्तव में सिर्फ एक है "जोड़ें" ऑपरेशन और "निकालें" ऑपरेशन। आप मान प्राप्त नहीं कर सकते हैं, आप मान सेट नहीं कर सकते हैं, आप इसे कॉल नहीं कर सकते हैं - आप ईवेंट (add) के लिए केवल एक हैंडलर सदस्यता ले सकते हैं या एक सदस्यता रद्द कर सकते हैं (remove)। यह ठीक है - यह encapsulation, सादा और सरल है। उचित रूप से जोड़ने/निकालने के लिए प्रकाशक पर निर्भर है, लेकिन जब तक प्रकाशक विवरण उपलब्ध कराने का विकल्प नहीं चुनता है, तो ग्राहक कार्यान्वयन-विशिष्ट भागों को संशोधित या एक्सेस नहीं कर सकते हैं।

फील्ड-तरह की घटनाओं सी # में (जहां ऐड निर्दिष्ट नहीं करते/बिट्स निकालने के लिए) इस छिपाने - वे एक प्रतिनिधि प्रकार और एक घटना का एक चर बनाने के। ईवेंट के ऐड/निकालें कार्यान्वयन केवल ग्राहकों का ट्रैक रखने के लिए चर का उपयोग करते हैं।

कक्षा के अंदर आप चर के संदर्भ में हैं (इसलिए आप वर्तमान में सब्सक्राइब किए गए प्रतिनिधि प्राप्त कर सकते हैं, उन्हें निष्पादित कर सकते हैं) और कक्षा के बाहर आप ईवेंट को स्वयं ही संदर्भित करते हैं (इसलिए केवल क्षमताओं को जोड़/हटाएं)।

फ़ील्ड जैसी घटनाओं का विकल्प वह जगह है जहां आप स्वयं को जोड़ने/निकालने के लिए स्पष्ट रूप से कार्यान्वित करते हैं, उदा।

private EventHandler clickHandler; // Normal private field 

public event EventHandler Click 
{ 
    add 
    { 
     Console.WriteLine("New subscriber"); 
     clickHandler += value; 
    } 
    remove 
    { 
     Console.WriteLine("Lost a subscriber"); 
     clickHandler -= value; 
    } 
} 

अधिक जानकारी के लिए my article on events देखें। आप ClickHandlers की तरह एक संपत्ति लिखना वर्तमान बहु डाली प्रतिनिधि वापस जाने के लिए कर सकता है, या HasClickHandlers वहाँ किसी भी हैं या नहीं लौटने के लिए -

पाठ्यक्रम घटना प्रकाशक भी अधिक जानकारी उपलब्ध करा सकते हैं के

। हालांकि यह कोर इवेंट मॉडल का हिस्सा नहीं है।

+0

+1। बस इसमें जोड़ने के लिए, आप निश्चित रूप से, बहु-कलाकार प्रतिनिधि के साथ स्पष्ट तरीकों के रूप में ऐड/निकालें ऑपरेशंस को कार्यान्वित कर सकते हैं। इस मामले में, आप ग्राहकों और गैर-ग्राहकों को स्वयं ट्रैक कर सकते हैं - और इस प्रकार यदि आप चुनते हैं तो अतिरिक्त जानकारी तक पहुंच प्रदान करें (जैसे ग्राहकों की गिनती आदि)। – LBushkin

+0

यूप - वह जोड़ देगा, धन्यवाद। –

+0

यह ध्यान देने योग्य हो सकता है कि * कारण * घटनाएं इस तरह कार्यान्वित की गई हैं ताकि किसी वर्ग को घटना के साथ कक्षा के व्यवहार को आसानी से नहीं बदला जा सके (जैसे एक प्रतिनिधि संपत्ति की तरह) और इसे मिटा देना ग्राहकों की सूची। – womp

0

आपको बेस क्लास से ऐसा करना होगा। यही कारण है कि आपने यह किया:

protected void OnSomeEvent(EventArgs e) { 
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
    if (SomeEvent != null) SomeEvent(this, e); 
} 

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

+2

ध्यान रखें कि यह थ्रेडसेफ नहीं है। यदि परीक्षण के बाद अंतिम हथियार हटा दिया जाता है लेकिन आमंत्रण से पहले यह एक NullReferenceException फेंक सकता है। –

+0

@ जोन - मैंने इसके बारे में आपका लेख पढ़ा है, लेकिन मुझे नहीं लगता कि यह इस वेब ऐप में होगा। – gabe

2

यहाँ एक अलग सवाल

क्या मान शून्य के लिए बाह्य रूप में परिभाषित किया गया घटना की जांच में है क्या है?

एक घटना का एक बाहरी उपभोक्ता आप केवल कर सकते हैं के रूप में 2 आपरेशन

  • हैंडलर
  • जोड़े हैंडलर

निकालें घटना के अशक्त या गैर nullness है इन 2 कार्यों पर कोई असर नहीं। आप एक परीक्षा क्यों चलाना चाहते हैं जो कोई समझदार मूल्य प्रदान न करे?

+0

मैं 2 खराब डिजाइन किए गए तीसरे पक्ष के वेब नियंत्रणों के लिए एक रैपर कार्य-आसपास कक्षा लिखने की कोशिश कर रहा हूं जिसे मुझे एक परियोजना के लिए उपयोग करने की आवश्यकता है (मुझे इस पागल नियंत्रण से नफरत है)। मैं एक वर्ग (रैपर) के सदस्य ऑब्जेक्ट (वेब ​​नियंत्रण में से किसी एक के संदर्भ में) में किसी ईवेंट के लिए इवेंट हैंडलर के रूप में एक विशिष्ट विधि के उपयोग को लागू करने का प्रयास कर रहा हूं। मैं रैपर वर्ग में एक चेक करना चाहता हूं ताकि यह सुनिश्चित किया जा सके कि रैपर वर्ग के उपयोगकर्ता ने सदस्य ऑब्जेक्ट की स्थिति में एक विशिष्ट ईवेंट हैंडलर जोड़ा हो। यह इस 2 बेवकूफ नियंत्रणों के लिए बहुत विशिष्ट है और समझाने में कठोर है ... – gabe

1

'ईवेंट' कीवर्ड का उपयोग करते समय यह एक नियम है।जब आप कोई ईवेंट बनाते हैं, तो आप प्रतिनिधि के साथ "सब्सक्राइब/सदस्यता समाप्त" संबंध में बाहरी कक्षा के संपर्क को प्रतिबंधित कर रहे हैं, इसमें विरासत के मामले शामिल हैं। याद रखें एक घटना अनिवार्य रूप से एक संपत्ति है, लेकिन विधि कॉल के लिए, यह वास्तव में एक वस्तु ही नहीं है, तो वास्तव में इसे और अधिक इस तरह दिखता है:

public event SomeEventHandler SomeEvent 
{ 
    add 
    { 
      //Add method call to delegate 
    } 
    remove 
    { 
      //Remove method call to delegate 
    } 
} 
14

आप आसानी से एक बहुत ही सरल दृष्टिकोण यहां बार-बार सदस्यता नहीं करने के लिए उपयोग कर सकते हैं एक घटना के लिए।

2 दृष्टिकोण नीचे किया जा सकता है की कोई एक:

  1. करें दृष्टिकोण: _getWarehouseForVendorCompletedSubscribed एक निजी चर गलत पर प्रारंभ है।

    if (!_getWarehouseForVendorCompletedSubscribed) 
        { 
         _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted); 
         _getWarehouseForVendorCompletedSubscribed = true; 
        } 
    
  2. सदस्यता समाप्त दृष्टिकोण: सदस्यता रद्द हर आप सदस्यता के लिए चाहते हैं को शामिल करें।

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs> 
    (_serviceClient_GetWarehouseForVendorCompleted); 
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
         EventHandler<GetWarehouseForVendorCompletedEventArgs> 
         (_serviceClient_GetWarehouseForVendorCompleted); 
    
+0

+1 यह ब्रिलियंट है और यह वही है जो मैं ढूंढ रहा था।मुझे समझ में नहीं आता कि इसे अधिक वोट क्यों नहीं मिला –

+0

सदस्यता रद्द करने के दृष्टिकोण के लिए, यदि आप पहले से जोड़े नहीं गए हैं तो आप एक नया EventHandler कैसे हटा सकते हैं? – noahnu

+3

यदि इसे जोड़ा नहीं गया है, तो यह केवल - = को अनदेखा कर देगा –

3

यहाँ जवाब:

using System; 
delegate void MyEventHandler(); 
class MyEvent 
{ 
    string s; 
    public event MyEventHandler SomeEvent; 
    // This is called to raise the event. 
    public void OnSomeEvent() 
    { 
     if (SomeEvent != null) 
     { 
      SomeEvent(); 
     } 

    } 

    public string IsNull 
    { 
     get 
     { 
      if (SomeEvent != null) 
       return s = "The EventHandlerList is not NULL"; 
      else return s = "The EventHandlerList is NULL"; ; 
     } 
    } 
} 

class EventDemo 
{ 
    // An event handler. 
    static void Handler() 
    { 
     Console.WriteLine("Event occurred"); 
    } 

    static void Main() 
    { 
     MyEvent evt = new MyEvent(); 
     // Add Handler() to the event list. 
     evt.SomeEvent += Handler; 
     // Raise the event. 
     //evt.OnSomeEvent(); 
     evt.SomeEvent -= Handler; 
     Console.WriteLine(evt.IsNull); 
     Console.ReadKey(); 
    } 
} 
0

घटना के प्रकाशक परोक्ष केवल += और -= संचालन ओवरलोड, और अन्य कार्यों क्योंकि स्पष्ट कारणों के प्रकाशक में लागू नहीं कर रहे हैं जैसा कि ऊपर बताया , जैसे घटनाओं को बदलने के लिए ग्राहक को नियंत्रण देना नहीं चाहते हैं।

यदि हम ग्राहक वर्ग में कोई विशेष ईवेंट सब्सक्राइब किया गया है तो सत्यापित करना चाहते हैं, तो बेहतर प्रकाशक अपनी कक्षा में एक ध्वज सेट करेगा जब ईवेंट ग्राहक होगा और जब यह अनसब्सक्राइबर होगा तो ध्वज साफ़ करें।

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