मैं C # में ईवेंट सदस्यता कैसे साफ़ कर सकता हूं?


141

निम्नलिखित C # वर्ग लें:

c1 {
 event EventHandler someEvent;
}

अगर वहाँ के लिए सदस्यता के एक बहुत हैं c1की someEventघटना और मैं उन सभी को स्पष्ट करना चाहते हैं, सबसे अच्छा तरीका है इस लक्ष्य को हासिल करने के लिए क्या है? यह भी विचार करें कि इस घटना के लिए सदस्यता लंबित / गुमनाम प्रतिनिधि हो सकते हैं।

वर्तमान में मेरा समाधान यह ResetSubscriptions()है c1कि someEventशून्य पर सेट करने के लिए एक विधि जोड़ना है । मुझे नहीं पता कि इसका कोई अनदेखी परिणाम है या नहीं।

जवाबों:


181

कक्षा के भीतर से, आप (छिपे हुए) चर को अशक्त करने के लिए सेट कर सकते हैं। एक रिक्त संदर्भ प्रभावी रूप से एक खाली मंगलाचरण सूची का प्रतिनिधित्व करने का विहित तरीका है।

कक्षा के बाहर से, आप ऐसा नहीं कर सकते - घटनाएँ मूल रूप से "सदस्यता" और "सदस्यता समाप्त करें" को उजागर करती हैं और यही है।

यह इस बात से अवगत होने के लायक है कि वास्तव में क्षेत्र-जैसी घटनाएँ क्या कर रही हैं - वे एक ही समय में एक चर और एक घटना बना रहे हैं । वर्ग के भीतर, आप चर का संदर्भ देते हैं। बाहर से, आप घटना का संदर्भ देते हैं।

अधिक जानकारी के लिए घटनाओं और प्रतिनिधियों पर मेरा लेख देखें ।


3
यदि आप जिद्दी हैं, तो आप इसे प्रतिबिंब के माध्यम से स्पष्ट कर सकते हैं। Stackoverflow.com/questions/91778/… देखें ।
ब्रायन

1
@ ब्रायन: यह कार्यान्वयन पर निर्भर करता है। यदि यह केवल एक फ़ील्ड-जैसी घटना या ए है EventHandlerList, तो आप सक्षम हो सकते हैं। आपको हालांकि उन दो मामलों को पहचानना होगा - और अन्य कार्यान्वयन की संख्या हो सकती है।
जॉन स्कीट

@ जोशुआ: नहीं, यह चर को शून्य मान देगा। मैं मानता हूं कि चर को नहीं बुलाया जाएगा hidden
जॉन स्कीट

@JonSkeet यही मैंने (सोचा) मैंने कहा था। जिस तरह से लिखा गया था उसने मुझे 5 मिनट तक भ्रमित किया।

@ जोशुआलमुस्गा: आपने कहा था कि यह एक आह्वान सूची को साफ करेगा, जो किसी मौजूदा वस्तु को संशोधित करने जैसा लगता है।
जॉन स्कीट

34

C1 में एक विधि जोड़ें जो 'someEvent' को अशक्त करने के लिए सेट करेगी।

public class c1
{
    event EventHandler someEvent;
    public ResetSubscriptions() => someEvent = null;    
}

वह व्यवहार मैं देख रहा हूं। जैसा कि मैंने अपने प्रश्न में कहा था, मुझे नहीं पता कि मैं कुछ देख रहा हूं।
प्रोग्रामर

8
class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() => someEvent = delegate { };
}

यह उपयोग करने के लिए बेहतर है delegate { }की तुलना में nullअशक्त रेफरी अपवाद से बचने के लिए।


2
क्यों? क्या आप इस उत्तर पर विस्तार कर सकते हैं?
एस। बुडा

1
@ एस.बुडा क्योंकि अगर यह अशक्त है तो आपको एक अशक्त रेफरी मिलेगा। यह एक List.Clear()बनाम का उपयोग करने जैसा है myList = null
ऑस्टिनवेरीयन

6

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


6

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

कृपया पुस्तक देखें - C # 4.0 संक्षेप में, पृष्ठ 125।

यहाँ कुछ ने Delegate.RemoveAllविधि का उपयोग करने का प्रस्ताव दिया । यदि आप इसका उपयोग करते हैं, तो नमूना कोड नीचे दिए गए फॉर्म का अनुसरण कर सकता है। लेकिन यह वास्तव में बेवकूफी है। सिर्फ फंक्शन के SomeEvent=nullअंदर ही क्यों ClearSubscribers()?

public void ClearSubscribers ()
{
   SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
   // Then you will find SomeEvent is set to null.
}

5

आप Delegate.Remove या Delegate.RemoveAll विधियों का उपयोग करके इसे प्राप्त कर सकते हैं।


6
मुझे विश्वास नहीं है कि यह लंबोदर भावों या अनाम प्रतिनिधियों के साथ काम करेगा।
प्रोग्रामर

3

वैचारिक विस्तारित उबाऊ टिप्पणी।

मैं "इवेंट हैंडलर" शब्द का उपयोग "इवेंट" या "डेलिगेट" के बजाय करता हूं। और अन्य सामान के लिए "घटना" शब्द का इस्तेमाल किया। कुछ प्रोग्रामिंग भाषाओं में (VB.NET, Object Pascal, Objective-C), "इवेंट" को "संदेश" या "सिग्नल" कहा जाता है, और यहां तक ​​कि "संदेश" कीवर्ड, और विशिष्ट चीनी सिंटैक्स भी होता है।

const
  WM_Paint = 998;  // <-- "question" can be done by several talkers
  WM_Clear = 546;

type
  MyWindowClass = class(Window)
    procedure NotEventHandlerMethod_1;
    procedure NotEventHandlerMethod_17;

    procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
    procedure DoClearEventHandler; message WM_Clear;
  end;

और, उस "संदेश" का जवाब देने के लिए, एक "इवेंट हैंडलर" प्रतिक्रिया देता है, चाहे वह एक एकल प्रतिनिधि या कई प्रतिनिधि हों।

सारांश: "ईवेंट" "प्रश्न" है, "ईवेंट हैंडलर (एस)" ​​उत्तर हैं।


1

यह मेरा समाधान है:

public class Foo : IDisposable
{
    private event EventHandler _statusChanged;
    public event EventHandler StatusChanged
    {
        add
        {
            _statusChanged += value;
        }
        remove
        {
            _statusChanged -= value;
        }
    }

    public void Dispose()
    {
        _statusChanged = null;
    }
}

आपको आह्वान सूची के सभी सदस्यों को सदस्यता समाप्त करने के लिए कॉल Dispose()या उपयोग करने की आवश्यकता है using(new Foo()){/*...*/}


0

सभी ईवेंट हटाएं, मान लें कि ईवेंट "एक्शन" प्रकार है:

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}

1
यदि आप उस प्रकार के अंदर हैं, जिसे आपने ईवेंट घोषित करने की आवश्यकता नहीं है, तो आप इसे केवल अशक्त करने के लिए सेट कर सकते हैं, यदि आप टाइप के बाहर हैं, तो आप प्रतिनिधि की मंगलाचरण सूची प्राप्त नहीं कर सकते। यदि कॉल करते समय भी घटना शून्य है, तो भी, आपका कोड एक अपवाद फेंकता है GetInvocationList
सेवाकाल

-1

मैन्युअल रूप से कॉलबैक को जोड़ने और हटाने के बजाय, हर जगह घोषित प्रतिनिधि प्रकार का एक गुच्छा होने पर:

// The hard way
public delegate void ObjectCallback(ObjectType broadcaster);

public class Object
{
    public event ObjectCallback m_ObjectCallback;
    
    void SetupListener()
    {
        ObjectCallback callback = null;
        callback = (ObjectType broadcaster) =>
        {
            // one time logic here
            broadcaster.m_ObjectCallback -= callback;
        };
        m_ObjectCallback += callback;

    }
    
    void BroadcastEvent()
    {
        m_ObjectCallback?.Invoke(this);
    }
}

आप इस सामान्य दृष्टिकोण की कोशिश कर सकते हैं:

public class Object
{
    public Broadcast<Object> m_EventToBroadcast = new Broadcast<Object>();

    void SetupListener()
    {
        m_EventToBroadcast.SubscribeOnce((ObjectType broadcaster) => {
            // one time logic here
        });
    }

    ~Object()
    {
        m_EventToBroadcast.Dispose();
        m_EventToBroadcast = null;
    }

    void BroadcastEvent()
    {
        m_EventToBroadcast.Broadcast(this);
    }
}


public delegate void ObjectDelegate<T>(T broadcaster);
public class Broadcast<T> : IDisposable
{
    private event ObjectDelegate<T> m_Event;
    private List<ObjectDelegate<T>> m_SingleSubscribers = new List<ObjectDelegate<T>>();

    ~Broadcast()
    {
        Dispose();
    }

    public void Dispose()
    {
        Clear();
        System.GC.SuppressFinalize(this);
    }

    public void Clear()
    {
        m_SingleSubscribers.Clear();
        m_Event = delegate { };
    }

    // add a one shot to this delegate that is removed after first broadcast
    public void SubscribeOnce(ObjectDelegate<T> del)
    {
        m_Event += del;
        m_SingleSubscribers.Add(del);
    }

    // add a recurring delegate that gets called each time
    public void Subscribe(ObjectDelegate<T> del)
    {
        m_Event += del;
    }

    public void Unsubscribe(ObjectDelegate<T> del)
    {
        m_Event -= del;
    }

    public void Broadcast(T broadcaster)
    {
        m_Event?.Invoke(broadcaster);
        for (int i = 0; i < m_SingleSubscribers.Count; ++i)
        {
            Unsubscribe(m_SingleSubscribers[i]);
        }
        m_SingleSubscribers.Clear();
    }
}

क्या आप अपने प्रश्न को प्रारूपित कर सकते हैं और बाईं ओर सभी सफेद स्थान को हटा सकते हैं? जब आप एक IDE से कॉपी और पेस्ट करते हैं तो ऐसा हो सकता है
AustinWBryan

बस उस सफेद स्थान से छुटकारा पा लिया, मेरा बुरा
बर्थडामन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.