2009-03-23 17 views
24

कोई ईवेंट हैंडलर शून्य होने पर जांच कर रहा है, क्या यह प्रति-थ्रेड आधार पर किया जाता है? कारण है कि मैं एक अशक्त चेक (code taken from this site) की आवश्यकता होगीइवेंट हैंडलर में शून्य जांच का उपयोग

EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen); 

मैं ऊपर जहाँ मैं अशक्त के लिए जाँच इस पैटर्न निम्न कोड जोड़ है, तो:

सुनिश्चित करना किसी के लिए घटना को इस तरह से किया जाता है सुन रहा है। मैं क्या खो रहा हूँ?

इसके अलावा, घटनाओं और जीसी के साथ नियम क्या है?

+3

देखें: http://www.dailycoding.com/Posts/avoiding_event__null_check.aspx विस्तारित स्पष्टीकरण के लिए। –

उत्तर

44

यह वास्तव में स्पष्ट नहीं है कि आपका क्या मतलब है मुझे डर है, लेकिन यदि प्रतिनिधि की कमी की संभावना है, तो आपको प्रत्येक धागे पर अलग से जांच करनी होगी। आमतौर पर आप ऐसा चाहते हैं:

public void OnSeven() 
{ 
    DivBySevenHandler handler = EventSeven; 
    if (handler != null) 
    { 
     handler(...); 
    } 
} 

यह सुनिश्चित करता है कि OnSeven() के दौरान भले ही EventSeven परिवर्तन आप एक NullReferenceException नहीं मिलेगा।

लेकिन आप सही हैं कि अगर आपको निश्चित रूप से सब्सक्राइब किए गए हैंडलर मिलते हैं तो आपको शून्य जांच की आवश्यकता नहीं है। यह आसानी से एक "कोई-op" हैंडलर के साथ सी # 2 में किया जा सकता:

public event DivBySevenHandler EventSeven = delegate {}; 

दूसरी ओर, आप पराक्रम सिर्फ ताला लगा के कुछ प्रकार चाहते हैं कि आप "नवीनतम मिल गया है सुनिश्चित करने के लिए "हैंडलर का सेट, अगर आपको विभिन्न धागे से सदस्यता मिल सकती है। मेरे पास example in my threading tutorial है जो मदद कर सकता है - हालांकि आमतौर पर मैं इसकी आवश्यकता से बचने की कोशिश करने की सलाह देता हूं।

कचरा संग्रहण के संदर्भ में, घटना प्रकाशक घटना ग्राहक (अर्थात हैंडलर का लक्ष्य) के लिए एक संदर्भ के साथ समाप्त होता है। यह केवल एक समस्या है यदि प्रकाशक को ग्राहक से अधिक समय तक रहने का मतलब है।

+0

(OP के लिए)/सूची) आखिरी बिंदु पर, यह ** विशेष रूप से ** स्थिर घटनाओं के सत्य है (क्योंकि स्थिर क्षेत्र संग्रह के लिए कभी भी योग्य नहीं होता है)। इस कारण से स्टेटिक इवेंट सबसे अच्छे से बचाए जाते हैं। –

+0

मुझे आश्चर्य है कि क्या किसी इवेंट डिलीगेट श्रेणी के प्रतिनिधियों के पास कोई समस्या हो सकती है, जो एक शून्य प्रतिनिधि के रूप में वही अर्थशास्त्र होगा/छोड़कर/एक नल इवेंट डिलीगेट का आविष्कार स्पष्ट रूप से एक एनओपी के रूप में परिभाषित किया जाएगा। – supercat

+1

'इवेंटसेवन' के बारे में क्या? इन्वोकोक (....) '। क्या यह धागा सुरक्षित है? –

2

इसे फायर करने से पहले एक ईवेंट हैंडलर की जांच करना हमेशा अच्छा अभ्यास होता है। मैं यह भी करता हूं अगर मैं शुरुआत में खुद को "गारंटी" देता हूं कि यह हमेशा सेट होता है। अगर मैं बाद में इसे बदलता हूं तो मुझे अपने सभी आयोजन फायरिंग की जांच करने की ज़रूरत नहीं है। तो प्रत्येक घटना के लिए मैं हमेशा इस तरह एक साथ OnXXX विधि है:

private void OnEventSeven() 
{ 
    var handler = EventSeven; 
    if (handler != null) 
    { 
     handler(this, EventArgs.Empty); 
    } 
} 

यह खासकर तब महत्वपूर्ण है, तो ईवेंट हैंडलर के बाद से बाहरी कॉल जोड़ सकते हैं और इच्छा पर ईवेंट हैंडलर्स को हटा सकते हैं अपने वर्ग के लिए सार्वजनिक है।

+2

यह थ्रेड-सुरक्षित नहीं है। यदि अंतिम हैंडलर "if" का मूल्यांकन करने के बाद सदस्यता रद्द करता है, तो आप NullReferenceException के साथ समाप्त हो सकते हैं। –

+0

मैन, हम कैसे आते हैं कि हममें से इतने सारे लोग थ्रेड-यूएन-सुरक्षा को नहीं देख पाए। Wakeup कॉल के लिए धन्यवाद! –

+0

ऐसा लगता है कि हममें से कई लोगों द्वारा समान (एंटी) पैटर्न का उपयोग किया जा रहा है। लेकिन इस मामले में, पीटर मार्क के रूप में कर रहा है (संदर्भ को कैप्चर कर रहा है, लेकिन एक अंतर्निहित प्रकार का उपयोग कर रहा है)। मार्क ने क्या लिखा और पीटर के बीच क्या अंतर है? – dotnetdev

0

आप इसका मतलब यह हैं:

public static void OnEventSeven(DivBySevenEventArgs e) 
    { 
     if(EventSeven!=null) 
      EventSeven(new object(),e); 
    }  

कोड का टुकड़ा है, तो जवाब है:

कोई भी नहीं "EventSeven" ईवेंट हैंडलर का सदस्य बनता है तो आप पर एक अशक्त-संदर्भ अपवाद मिल जाएगा "इवेंटसेवन (नई ऑब्जेक्ट(), ई);"

और नियम:

ग्राहक हैंडलर (+ =) जोड़ने और इसे हटाने के लिए जिम्मेदार है (- =) जब वह अब किसी भी घटनाओं प्राप्त करने के लिए नहीं है चाहता हूँ। कचरा संग्रह डिफ़ॉल्ट नियमों द्वारा जाता है, यदि किसी ऑब्जेक्ट का संदर्भ नहीं दिया जाता है तो इसे साफ़ किया जा सकता है।

+3

यह थ्रेड-सुरक्षित नहीं है। यदि "हैंड" का मूल्यांकन करने के बाद अंतिम हैंडलर सदस्यता रद्द करता है, तो आप NullreferenceException के साथ समाप्त हो सकते हैं। –

+0

सच है ... यह उदाहरण पूरी जगह पर है (और मैं इसका भी उपयोग करता हूं), इस धागे की सुरक्षा पर अभी भी खड़ा नहीं था! – thijs

+2

एक तरफ, क्या यह प्रेषक के लिए एक यादृच्छिक नव निर्मित वस्तु के बजाय घटना (यानी 'यह') शुरू करने वाली वस्तु नहीं है? –

47

समस्या यह है कि यदि कोई भी घटना की सदस्यता नहीं लेता है, तो यह शून्य है। और आप एक शून्य के खिलाफ नहीं आ सकते हैं। तीन दृष्टिकोण मन में छलांग:

  • अशक्त के लिए चेक (नीचे देखें)
  • एक "कुछ भी नहीं" हैंडलर जोड़ने:

जब जाँच public event EventHandler MyEvent = delegate {};

  • एक विस्तार विधि का उपयोग करें (नीचे देखें) शून्य के लिए, थ्रेड-सुरक्षित होने के लिए, आपको सिद्धांत में पहले प्रतिनिधि संदर्भ पर कब्जा कर लेना चाहिए (यदि यह चेक और आवेषण के बीच बदलता है):

    protected virtual void OnMyEvent() { 
        EventHandler handler = MyEvent; 
        if(handler != null) handler(this, EventArgs.Empty); 
    } 
    

    एक्सटेंशन तरीकों असामान्य संपत्ति है कि वे अशक्त उदाहरणों पर प्रतिदेय हैं ...

    public static void SafeInvoke(this EventHandler handler, object sender) 
        { 
         if (handler != null) handler(sender, EventArgs.Empty); 
        } 
        public static void SafeInvoke<T>(this EventHandler<T> handler, 
         object sender, T args) where T : EventArgs 
        { 
         if (handler != null) handler(sender, args); 
        } 
    

    तो आप कॉल कर सकते हैं है:

    MyEvent.SafeInvoke(this); 
    

    और यह दोनों अशक्त सुरक्षित (चेक के माध्यम से है) और थ्रेड-सुरक्षित (संदर्भ केवल एक बार पढ़कर)।

  • +2

    द्वारा प्रदान किए गए लिंक से कोड की प्रतिलिपि बनाई है, जो स्टैक – ShuggyCoUk

    +0

    पर स्पष्टीकरण प्रतिलिपि के माध्यम से थ्रेड सुरक्षा की तरह है। जब आप "केवल पढ़ने के द्वारा ...." कहते हैं, वह धागा सुरक्षित है क्योंकि एक बार पढ़ने के बाद, लक्ष्य (ऑब्जेक्ट जो पढ़ा जाता है - संदर्भ), स्वतंत्र रूप से बदल सकता है। लेकिन आप "संदर्भ को एक बार कैसे पढ़ते हैं"? शायद मैंने कुछ भी नहीं किया ... – dotnetdev

    +1

    @dotnetdev - क्योंकि हम इसे एक विधि तर्क के रूप में पास कर रहे हैं; यह वर्तमान मूल्य * स्टैक * पर पढ़ता है। सेफ इनवोक के अंदर, यह केवल स्टैक पर कॉपी को देखता है (मूल को 200 बार अपडेट किया जा सकता है, हम इसे कभी नहीं देख पाएंगे)। यह यहां मदद करता है कि प्रतिनिधि अपरिवर्तनीय हैं, इसलिए ... –

    0

    PostSharp का उपयोग कर संकलन चरण में संकलित असेंबली को समायोजित करना संभव है। यह आपको क्रॉस-कटिंग चिंताओं को हल करने, कोड में 'पहलुओं' लागू करने की अनुमति देता है।

    हालांकि शून्य जांच या खाली प्रतिनिधि प्रारंभिकता बहुत मामूली समस्या हो सकती है, मैंने एक पहलू लिखा जो असेंबली में सभी घटनाओं में एक खाली प्रतिनिधि जोड़कर इसे हल करता है।

    यह उपयोग काफी आसान है:

    [assembly: InitializeEventHandlers(AttributeTargetTypes = "Main.*")] 
    namespace Main 
    { 
        ... 
    } 
    

    मैं discussed the aspect in detail on my blog। मामले में आप PostSharp है, यहाँ पहलू है:

    /// <summary> 
    /// Aspect which when applied on an assembly or class, initializes all the event handlers (<see cref="MulticastDelegate" />) members 
    /// in the class(es) with empty delegates to prevent <see cref="NullReferenceException" />'s. 
    /// </summary> 
    /// <author>Steven Jeuris</author> 
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Event)] 
    [MulticastAttributeUsage(MulticastTargets.Event, AllowMultiple = false)] 
    [AspectTypeDependency(AspectDependencyAction.Commute, typeof(InitializeEventHandlersAttribute))] 
    [Serializable] 
    public class InitializeEventHandlersAttribute : EventLevelAspect 
    { 
        [NonSerialized] 
        Action<object> _addEmptyEventHandler; 
    
    
        [OnMethodEntryAdvice, MethodPointcut("SelectConstructors")] 
        public void OnConstructorEntry(MethodExecutionArgs args) 
        { 
         _addEmptyEventHandler(args.Instance); 
        } 
    
        // ReSharper disable UnusedMember.Local 
        IEnumerable<ConstructorInfo> SelectConstructors(EventInfo target) 
        { 
         return target.DeclaringType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
        } 
        // ReSharper restore UnusedMember.Local 
    
        public override void RuntimeInitialize(EventInfo eventInfo) 
        { 
         base.RuntimeInitialize(eventInfo); 
    
         // Construct a suitable empty event handler. 
         MethodInfo delegateInfo = DelegateHelper.MethodInfoFromDelegateType(eventInfo.EventHandlerType); 
         ParameterExpression[] parameters = delegateInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType)).ToArray(); 
         Delegate emptyDelegate 
          = Expression.Lambda(eventInfo.EventHandlerType, Expression.Empty(), "EmptyDelegate", true, parameters).Compile(); 
    
         // Create a delegate which adds the empty handler to an instance. 
         _addEmptyEventHandler = instance => eventInfo.AddEventHandler(instance, emptyDelegate); 
        } 
    } 
    

    ... और सहायक विधि का उपयोग करता है:

    /// <summary> 
    /// The name of the Invoke method of a Delegate. 
    /// </summary> 
    const string InvokeMethod = "Invoke"; 
    
    
    /// <summary> 
    /// Get method info for a specified delegate type. 
    /// </summary> 
    /// <param name = "delegateType">The delegate type to get info for.</param> 
    /// <returns>The method info for the given delegate type.</returns> 
    public static MethodInfo MethodInfoFromDelegateType(Type delegateType) 
    { 
        Contract.Requires(delegateType.IsSubclassOf(typeof(MulticastDelegate)), "Given type should be a delegate."); 
    
        return delegateType.GetMethod(InvokeMethod); 
    } 
    
    18

    मैं एक बहुत पुरानी पोस्ट बाहर खुदाई कर रहा हूँ - लेकिन मैं सिर्फ करना चाहते हैं सी # 6 के बारे में कुछ छोटी जानकारी संलग्न करें।0-सिंटेक्स:

    अब यह इस को बदलने के लिए संभव है:

    var handler = EventSeven; 
    
    if (handler != null) 
        handler.Invoke(this, EventArgs.Empty); 
    
    इस के साथ

    :

    handler?.Invoke(this, EventArgs.Empty); 
    


    संपादित करें: यह अभिव्यक्ति शरीर सदस्यों साथ संयोजन, आप निम्न कोड को छोटा कर सकते हैं:

    protected virtual void OnMyEvent() 
    { 
        EventHandler handler = MyEvent; 
        handler?.Invoke(this, EventArgs.Empty); 
    } 
    
    एक एक लाइनर के लिए नीचे

    :

    protected virtual void OnMyEvent() => MyEvent?.Invoke(this, EventArgs.Empty); 
    


    See MSDN for more information about the null-conditional operator
    this blog अभिव्यक्ति शरीर सदस्यों के बारे में देखें

    +0

    मैं समझने की कोशिश कर रहा हूं कि 'MyEvent ?Invoke'' हैंडलर के रूप में सुरक्षित है? Envoke'। शून्य प्रचार परमाणु जांच है? – Teejay

    +1

    असल में, यह हां लगता है। https://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-invocation-with-c-6/ – Teejay

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