ईवेंट एक्शन <> इवेंट ईवेंटहैंडलर <>


144

क्या घोषित करने event Action<>और के बीच कोई अंतर है event EventHandler<>

यह मानने से कोई फर्क नहीं पड़ता कि वास्तव में किसी घटना ने किस वस्तु को उठाया।

उदाहरण के लिए:

public event Action<bool, int, Blah> DiagnosticsEvent;

बनाम

public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;

class DiagnosticsArgs : EventArgs
{
    public DiagnosticsArgs(bool b, int i, Blah bl)
    {...}
    ...
}

उपयोग दोनों मामलों में लगभग समान होगा:

obj.DiagnosticsEvent += HandleDiagnosticsEvent;

कई चीजें हैं जो मुझे event EventHandler<>पैटर्न के बारे में पसंद नहीं हैं :

  • EventArgs से प्राप्त अतिरिक्त प्रकार की घोषणा
  • ऑब्जेक्ट स्रोत के अनिवार्य गुजर - अक्सर कोई परवाह नहीं करता है

अधिक कोड का मतलब बिना किसी स्पष्ट लाभ के बनाए रखने के लिए अधिक कोड है।

नतीजतन, मैं पसंद करता हूं event Action<>

हालांकि, केवल अगर एक्शन <> में कई प्रकार के तर्क हैं, तो एक अतिरिक्त वर्ग की आवश्यकता होगी।


2
plusOne (मैं सिर्फ सिस्टम को हराता हूं) "कोई परवाह नहीं करता"
hyankov

@plusOne: मैं वास्तव में भेजने वाले को जानने की जरूरत है! कुछ कहें और आप जानना चाहते हैं कि यह किसने किया। आपको 'ऑब्जेक्ट स्रोत' (उर्फ प्रेषक) की आवश्यकता थी।
कामरान बिगली

प्रेषक घटना के पेलोड में एक संपत्ति हो सकता है
थानासिस आयोनिडिस

जवाबों:


67

मुख्य अंतर यह होगा कि यदि आप Action<>अपने ईवेंट का उपयोग करते हैं, तो सिस्टम में वस्तुतः किसी अन्य ईवेंट के डिज़ाइन पैटर्न का पालन नहीं करेंगे, जिसे मैं एक दोष मानता हूं।

डोमिनेटिंग डिज़ाइन पैटर्न (सामर्थ्य के अलावा) के साथ एक उल्टा यह है कि आप EventArgsइवेंट के हस्ताक्षर में बदलाव किए बिना ऑब्जेक्ट को नए गुणों के साथ बढ़ा सकते हैं । यह तब भी संभव होगा यदि आप उपयोग करते हैं Action<SomeClassWithProperties>, लेकिन मैं वास्तव में उस मामले में नियमित दृष्टिकोण का उपयोग नहीं करने के साथ बिंदु नहीं देखता हूं।


Action<>मेमोरी लीक में परिणाम का उपयोग कर सकता है ? EventHandlerडिजाइन पैटर्न के साथ एक नकारात्मक पहलू मेमोरी लीक है। यह भी बताया जाना चाहिए कि कई ईवेंट हैंडलर हो सकते हैं लेकिन केवल एक एक्शन
ल्यूक टी ओ ब्रायन

4
@ ल्यूकोटी'ब्रायन: घटनाक्रम सार प्रतिनिधियों में हैं, इसलिए समान स्मृति रिसाव संभावनाएं मौजूद हैं Action<T>। इसके अलावा, एक कई तरीकों का उल्लेख Action<T> कर सकता है । यहाँ एक जिस्ट है
Fredrik

89

पिछले कुछ उत्तरों के आधार पर, मैं अपने उत्तर को तीन क्षेत्रों में तोड़ने जा रहा हूँ।

सबसे पहले, Action<T1, T2, T2... >एक व्युत्पन्न वर्ग के उपयोग से बनाम की भौतिक सीमाएं EventArgs। तीन हैं: सबसे पहले, यदि आप संख्या या प्रकार के मापदंडों को बदलते हैं, तो नए पैटर्न के अनुरूप हर विधि को बदलना होगा। यदि यह एक सार्वजनिक सामना करने वाली घटना है जिसे 3 पार्टी असेंबली का उपयोग कर रही है, और कोई भी पक्षधरता है कि घटना उत्पन्न होती है, तो यह एक कारण होगा कि संगतता के लिए इवेंट args से प्राप्त कस्टम वर्ग का उपयोग करने का एक कारण होगा (याद रखें, आप अभी भी CODD कर सकते हैं) ए Action<MyCustomClass>) का उपयोग करें , दूसरा, उपयोग Action<T1, T2, T2... >करने से आपको कॉलिंग विधि पर वापस जाने से रोक दिया जाएगा जब तक कि आपके पास किसी प्रकार की वस्तु नहीं है (उदाहरण के लिए एक हैंडल की गई संपत्ति) जो एक्शन के साथ पास की गई है। तीसरा, आपको नामित पैरामीटर नहीं मिलते हैं, इसलिए यदि आप 3 boolका ए int, दो पास कर रहे हैंstring'है, और एक DateTimeहै, तो आप पता नहीं क्या उन मूल्यों के अर्थ हैं। एक साइड नोट के रूप में, आप अभी भी "इस घटना को सुरक्षित रूप से उपयोग करते समय सुरक्षित रूप से आग लगा सकते हैं Action<T1, T2, T2... >"।

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

तीसरा, वास्तविक जीवन अभ्यास, मुझे व्यक्तिगत रूप से लगता है कि मैं संपत्ति परिवर्तन जैसी चीजों के लिए बहुत सारी घटनाओं को बंद करने की प्रवृत्ति रखता हूं, जो कि मुझे विशेष रूप से एमवीवीएम को देखने के मॉडल (जो एक दूसरे के साथ बातचीत करते हैं) के साथ या जहां घटना है एक एकल पैरामीटर। अधिकांश समय ये आयोजन public event Action<[classtype], bool> [PropertyName]Changed;या के रूप में होते हैं public event Action SomethingHappened;। इन मामलों में, दो लाभ हैं। सबसे पहले, मुझे जारी करने वाले वर्ग के लिए एक प्रकार मिलता है। यदि MyClassघोषित होता है और घटना को फायर करने वाला एकमात्र वर्ग होता है, तो मुझे MyClassइवेंट हैंडलर में काम करने का एक स्पष्ट उदाहरण मिलता है । दूसरी बात, साधारण घटनाओं जैसे कि संपत्ति में बदलाव की घटनाओं के लिए, मापदंडों का अर्थ स्पष्ट होता है और ईवेंट हैंडलर के नाम से बताया जाता है और मुझे इस प्रकार के ईवेंटों के लिए असंख्य वर्गों का निर्माण नहीं करना पड़ता है।


बहुत बढ़िया ब्लॉग पोस्ट। निश्चित रूप से पढ़ने लायक अगर आप इस धागे को पढ़ रहे हैं!
विक्सिर

1
विस्तृत और सुविचारित उत्तर जो निष्कर्ष के पीछे तर्क को स्पष्ट करता है
मिक टीटी

18

अधिकांश भाग पर, मैं कहता हूं कि पैटर्न का पालन करें। मैं है विशिष्ट कारणों के लिए इसे से भटक, लेकिन बहुत कम ही है, और। इस मामले में, मेरे पास सबसे बड़ा मुद्दा यह है कि मैं शायद अभी भी एक का उपयोग करूंगा Action<SomeObjectType>, जिससे मुझे बाद में अतिरिक्त गुण जोड़ने की अनुमति मिलेगी, और सामयिक 2-तरफा संपत्ति का उपयोग करें (विचार करें Handled, या अन्य प्रतिक्रिया-घटनाएं जहां सब्सक्राइबर को इवेंट ऑब्जेक्ट पर प्रॉपर्टी सेट करने की आवश्यकता होती है )। और एक बार जब आप उस लाइन को शुरू कर देते हैं, तो आप EventHandler<T>कुछ के लिए भी उपयोग कर सकते हैं T


14

वर्डियर एप्रोच का फायदा तब मिलता है जब आपका कोड 300,000 लाइन प्रोजेक्ट के अंदर हो।

कार्रवाई का उपयोग करना, जैसा कि आपके पास है, मुझे यह बताने का कोई तरीका नहीं है कि बूल, इंट, और ब्लाह क्या हैं। यदि आपकी कार्रवाई एक ऐसी वस्तु है जो मापदंडों को परिभाषित करती है तो ठीक है।

एक EventHandler का उपयोग करना जो एक EventArgs चाहता था और यदि आप अपने डायग्नोस्टिक्सआर्ग उदाहरण को संपत्तियों के लिए गेटर्स के साथ पूरा करेंगे जो उनके उद्देश्य पर टिप्पणी करते हैं तो आप आवेदन अधिक समझ में आएंगे। इसके अलावा, कृपया टिप्पणी करें या पूरी तरह से डायग्नोस्टिक्सअर्स कंस्ट्रक्टर में तर्कों का नाम दें।


6

यदि आप मानक ईवेंट पैटर्न का पालन करते हैं, तो आप ईवेंट फायरिंग को सुरक्षित / आसान बनाने के लिए एक एक्सटेंशन विधि जोड़ सकते हैं। (यानी निम्नलिखित कोड में SafeFire () नामक एक विस्तार विधि शामिल है, जो अशक्त जांच करता है, साथ ही (स्पष्ट रूप से) एक अलग चर में घटना की नकल करते हुए सामान्य अशक्त जाति-स्थिति से सुरक्षित रहती है जो घटनाओं को प्रभावित कर सकती है।)

(हालांकि मैं दो तरह के दिमाग में हूं कि क्या आपको अशक्त वस्तुओं पर विस्तार विधियों का उपयोग करना चाहिए ...)

public static class EventFirer
{
    public static void SafeFire<TEventArgs>(this EventHandler<TEventArgs> theEvent, object obj, TEventArgs theEventArgs)
        where TEventArgs : EventArgs
    {
        if (theEvent != null)
            theEvent(obj, theEventArgs);
    }
}

class MyEventArgs : EventArgs
{
    // Blah, blah, blah...
}

class UseSafeEventFirer
{
    event EventHandler<MyEventArgs> MyEvent;

    void DemoSafeFire()
    {
        MyEvent.SafeFire(this, new MyEventArgs());
    }

    static void Main(string[] args)
    {
        var x = new UseSafeEventFirer();

        Console.WriteLine("Null:");
        x.DemoSafeFire();

        Console.WriteLine();

        x.MyEvent += delegate { Console.WriteLine("Hello, World!"); };
        Console.WriteLine("Not null:");
        x.DemoSafeFire();
    }
}

4
... क्या आप एक्शन <टी> के साथ ऐसा नहीं कर सकते? सेफफायर <टी> (यह एक्शन <टी> ईवेंट, टी ईवेंटवेंट्स) को काम करना चाहिए ... और "जहां" का उपयोग करने की आवश्यकता नहीं है
समुद्र तट

6

मुझे पता है कि यह सवाल 10 साल से अधिक पुराना है, लेकिन यह मुझे प्रतीत होता है कि न केवल सबसे स्पष्ट उत्तर को संबोधित नहीं किया गया है, लेकिन शायद यह वास्तव में सवाल से अच्छी तरह से स्पष्ट नहीं है कि कवर के तहत क्या होता है। इसके अलावा, लेट बाइंडिंग के बारे में अन्य प्रश्न हैं और इसका मतलब है कि प्रतिनिधियों और लैम्ब्डा के संबंध में (उस पर बाद में)।

सबसे पहले कमरे, जब चुनने के लिए में 800 पौंड हाथी / गोरिल्ला को संबोधित करने के eventबनाम Action<T>/ Func<T>:

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

एक घटना के उदाहरण के रूप में, निम्न प्रकार से एक छोटे कंसोल एप्लिकेशन का उपयोग करके घटनाओं के एक सरल और 'मानक' सेट को वायर करने देता है:

public delegate void FireEvent(int num);

public delegate void FireNiceEvent(object sender, SomeStandardArgs args);

public class SomeStandardArgs : EventArgs
{
    public SomeStandardArgs(string id)
    {
        ID = id;
    }

    public string ID { get; set; }
}

class Program
{
    public static event FireEvent OnFireEvent;

    public static event FireNiceEvent OnFireNiceEvent;


    static void Main(string[] args)
    {
        OnFireEvent += SomeSimpleEvent1;
        OnFireEvent += SomeSimpleEvent2;

        OnFireNiceEvent += SomeStandardEvent1;
        OnFireNiceEvent += SomeStandardEvent2;


        Console.WriteLine("Firing events.....");
        OnFireEvent?.Invoke(3);
        OnFireNiceEvent?.Invoke(null, new SomeStandardArgs("Fred"));

        //Console.WriteLine($"{HeightSensorTypes.Keyence_IL030}:{(int)HeightSensorTypes.Keyence_IL030}");
        Console.ReadLine();
    }

    private static void SomeSimpleEvent1(int num)
    {
        Console.WriteLine($"{nameof(SomeSimpleEvent1)}:{num}");
    }
    private static void SomeSimpleEvent2(int num)
    {
        Console.WriteLine($"{nameof(SomeSimpleEvent2)}:{num}");
    }

    private static void SomeStandardEvent1(object sender, SomeStandardArgs args)
    {

        Console.WriteLine($"{nameof(SomeStandardEvent1)}:{args.ID}");
    }
    private static void SomeStandardEvent2(object sender, SomeStandardArgs args)
    {
        Console.WriteLine($"{nameof(SomeStandardEvent2)}:{args.ID}");
    }
}

आउटपुट निम्नानुसार दिखेगा:

यहां छवि विवरण दर्ज करें

यदि आपने ऐसा ही किया Action<int>या Action<object, SomeStandardArgs>, आप केवल SomeSimpleEvent2और केवल देखेंगे SomeStandardEvent2

तो अंदर क्या चल रहा है event?

यदि हम विस्तार करते हैं FireNiceEvent, तो संकलक वास्तव में निम्नलिखित उत्पन्न कर रहा है (मैंने थ्रेड सिंक्रनाइज़ेशन के संबंध में कुछ विवरण छोड़ दिए हैं जो इस चर्चा के लिए प्रासंगिक नहीं है):

   private EventHandler<SomeStandardArgs> _OnFireNiceEvent;

    public void add_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
    {
        Delegate.Combine(_OnFireNiceEvent, handler);
    }

    public void remove_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
    {
        Delegate.Remove(_OnFireNiceEvent, handler);
    }

    public event EventHandler<SomeStandardArgs> OnFireNiceEvent
    {
        add
        {
            add_OnFireNiceEvent(value)
        }
        remove
        {
            remove_OnFireNiceEvent(value)

        }
    }

संकलक एक निजी प्रतिनिधि चर उत्पन्न करता है जो वर्ग नामस्थान में दिखाई नहीं देता है जिसमें यह उत्पन्न होता है। वह प्रतिनिधि जो सदस्यता प्रबंधन और देर से बाध्यकारी भागीदारी के लिए उपयोग किया जाता है, और जनता का सामना करने वाला इंटरफ़ेस परिचित +=और -=ऑपरेटर है जिसे हम सभी जानते हैं और प्यार करते हैं:)

आप FireNiceEventप्रतिनिधि के दायरे को संरक्षित में जोड़कर / हटाए हैंडलर्स के लिए कोड को अनुकूलित कर सकते हैं । यह अब डेवलपर्स को हुक में कस्टम हुक जोड़ने की अनुमति देता है, जैसे लॉगिंग या सुरक्षा हुक। यह वास्तव में कुछ बहुत ही शक्तिशाली सुविधाओं के लिए बनाता है जो अब उपयोगकर्ता भूमिकाओं के आधार पर सदस्यता के लिए अनुकूलित अभिगम्यता की अनुमति देता है, आदि क्या आप लैम्ब्डा के साथ ऐसा कर सकते हैं? (वास्तव में आप अभिव्यक्ति संकलन पेड़ों द्वारा कस्टम कर सकते हैं, लेकिन यह इस प्रतिक्रिया के दायरे से परे है)।

यहाँ कुछ प्रतिक्रियाओं से कुछ बिंदुओं को संबोधित करने के लिए:

  • आर्ग की सूची को बदलने और उससे Action<T>प्राप्त वर्ग में गुणों को बदलने के बीच 'भंगुरता' में वास्तव में कोई अंतर नहीं है EventArgs। या तो केवल एक संकलन परिवर्तन की आवश्यकता नहीं होगी, वे दोनों एक सार्वजनिक इंटरफ़ेस को बदल देंगे और संस्करण की आवश्यकता होगी। कोई फर्क नहीं।

  • जिसके संबंध में एक उद्योग मानक है, जो इस बात पर निर्भर करता है कि इसका उपयोग कहां और क्यों किया जा रहा है। Action<T>और ऐसा अक्सर IoC और DI में उपयोग किया जाता है, और eventअक्सर इसका उपयोग संदेश मार्ग जैसे GUI और MQ प्रकार के फ्रेमवर्क में किया जाता है। ध्यान दें कि मैंने अक्सर कहा , हमेशा नहीं ।

  • डेलिगेट्स में लंबोदर की तुलना में अलग-अलग जीवनकाल हैं। एक को पकड़ने के बारे में भी पता होना चाहिए ... न केवल बंद होने के साथ, बल्कि 'बिल्ली क्या खींचती है' की धारणा के साथ। यह स्मृति पदचिह्न / जीवनकाल और साथ ही प्रबंधन उर्फ ​​लीक को प्रभावित करता है।

एक और बात, कुछ मैंने पहले संदर्भित किया था ... देर से बाध्यकारी की धारणा। LINQ जैसे ढांचे का उपयोग करते समय, लैम्बडा 'लाइव' हो जाने के बारे में आपको अक्सर यह दिखाई देगा। यह एक प्रतिनिधि के देर से बाध्यकारी से बहुत अलग है, जो एक से अधिक बार हो सकता है (यानी लंबोदर हमेशा होता है, लेकिन बाध्यकारी मांग पर होता है जितनी बार जरूरत होती है), एक लंबो के विपरीत, जो एक बार होता है, इसका किया जाता है - जादू चला गया है, और विधि (एस) / संपत्ति (ies) हमेशा बांध देगी। मन में कुछ रखने के लिए।


4

मानक .NET ईवेंट पैटर्न को देखते हुए हम पाते हैं

.NET ईवेंट प्रतिनिधि के लिए मानक हस्ताक्षर है:

void OnEventRaised(object sender, EventArgs args);

[...]

तर्क सूची में दो तर्क होते हैं: प्रेषक और घटना तर्क। प्रेषक का संकलन समय प्रकार System.Object है, भले ही आप संभवतः अधिक व्युत्पन्न प्रकार जानते हैं जो हमेशा सही होगा। सम्मेलन द्वारा, वस्तु का उपयोग करें ।

नीचे उसी पृष्ठ पर हम विशिष्ट घटना परिभाषा का एक उदाहरण पाते हैं जो कुछ इस तरह है

public event EventHandler<EventArgs> EventName;

हमने परिभाषित किया था

class MyClass
{
  public event Action<MyClass, EventArgs> EventName;
}

हैंडलर हो सकता था

void OnEventRaised(MyClass sender, EventArgs args);

जहाँ senderसही ( अधिक व्युत्पन्न ) प्रकार है।


क्षमा करें कि टिप्पणी में अंतर नहीं है कि हैंडलर हस्ताक्षर में अंतर है, जो कि अधिक सटीक रूप से टाइप किए गए लाभ होगा sender
user1832484 14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.