如何清除 C# 中的事件订阅?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/153573/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:52:28  来源:igfitidea点击:

How can I clear event subscriptions in C#?

提问by programmer

Take the following C# class:

使用以下 C# 类:

c1 {
 event EventHandler someEvent;
}

If there are a lot of subscriptions to c1's someEventevent and I want to clear them all, what is the best way to achieve this? Also consider that subscriptions to this event could be/are lambdas/anonymous delegates.

如果c1'ssomeEvent事件有很多订阅,而我想全部清除它们,那么实现这一目标的最佳方法是什么?还要考虑对这个事件的订阅可能是/是 lambdas/匿名委托。

Currently my solution is to add a ResetSubscriptions()method to c1that sets someEventto null. I don't know if this has any unseen consequences.

目前我的解决方案是添加一个ResetSubscriptions()方法来c1设置someEvent为空。我不知道这是否有任何看不见的后果。

采纳答案by Jon Skeet

From within the class, you can set the (hidden) variable to null. A null reference is the canonical way of representing an empty invocation list, effectively.

在类中,您可以将(隐藏的)变量设置为 null。空引用是有效表示空调用列表的规范方式。

From outside the class, you can't do this - events basically expose "subscribe" and "unsubscribe" and that's it.

在课堂之外,你不能这样做——事件基本上公开了“订阅”和“取消订阅”,仅此而已。

It's worth being aware of what field-like events are actually doing - they're creating a variable andan event at the same time. Within the class, you end up referencing the variable. From outside, you reference the event.

了解类似场的事件实际上在做什么是值得的——它们同时创建了一个变量一个事件。在类中,您最终会引用变量。从外部,您引用该事件。

See my article on events and delegatesfor more information.

有关更多信息,请参阅我关于事件和委托的文章

回答by programmer

Add a method to c1 that will set 'someEvent' to null...

向 c1 添加一个方法,将“someEvent”设置为 null...

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

回答by Micah

You can achieve this by using the Delegate.Remove or Delegate.RemoveAll methods.

您可以通过使用 Delegate.Remove 或 Delegate.RemoveAll 方法来实现这一点。

回答by Jonathan C Dickinson

Setting the event to null inside the class works. When you dispose a class you should always set the event to null, the GC has problems with events and may not clean up the disposed class if it has dangling events.

在类中将事件设置为 null 有效。当您处理一个类时,您应该始终将事件设置为 null,GC 会出现事件问题,并且如果它有悬空事件,则可能不会清理已处理的类。

回答by umlcat

Conceptual extended boring comment.

概念性扩展无聊评论。

I rather use the word "event handler" instead of "event" or "delegate". And used the word "event" for other stuff. In some programming languages (VB.NET, Object Pascal, Objective-C), "event" is called a "message" or "signal", and even have a "message" keyword, and specific sugar syntax.

我宁愿使用“事件处理程序”这个词,而不是“事件”或“委托”。并将“事件”一词用于其他内容。在一些编程语言(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;

And, in order to respond to that "message", a "event handler" respond, whether is a single delegate or multiple delegates.

并且,为了响应该“消息”,“事件处理程序”响应,无论是单个委托还是多个委托。

Summary: "Event" is the "question", "event handler (s)" are the answer (s).

总结:“事件”是“问题”,“事件处理程序”是答案。

回答by Cary

The best practice to clear all subscribers is to set the someEvent to null by adding another public method if you want to expose this functionality to outside. This has no unseen consequences. The precondition is to remember to declare SomeEvent with the keyword 'event'.

清除所有订阅者的最佳做法是,如果您想将此功能公开给外部,则通过添加另一个公共方法将 someEvent 设置为 null。这没有看不见的后果。前提是记住用关键字'event'声明SomeEvent。

Please see the book - C# 4.0 in the nutshell, page 125.

请参阅本书 - C# 4.0 in the nutshell,第 125 页。

Some one here proposed to use Delegate.RemoveAllmethod. If you use it, the sample code could follow the below form. But it is really stupid. Why not just SomeEvent=nullinside the ClearSubscribers()function?

这里有人提议使用Delegate.RemoveAll方法。如果您使用它,示例代码可以遵循以下形式。但这真的很愚蠢。为什么不只是SomeEvent=nullClearSubscribers()函数内部?

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

回答by Googol

Remove all events, assume the event is an "Action" type:

删除所有事件,假设事件是“Action”类型:

Delegate[] dary = TermCheckScore.GetInvocationList();

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

回答by Feng

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

It is better to use delegate{} than null

使用 delegate{} 比使用 null 更好

回答by Jalal

This is my solution:

这是我的解决方案:

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

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

You need to call Dispose()or use using(new Foo()){/*...*/}pattern to unsubscribe all members of invocation list.

您需要调用Dispose()或使用using(new Foo()){/*...*/}模式来取消订阅调用列表的所有成员。

回答by barthdamon

Instead of adding and removing callbacks manually and having a bunch of delegate types declared everywhere:

而不是手动添加和删除回调并在各处声明一堆委托类型:

    // 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);
        }
    }

You could try this generic approach:

您可以尝试这种通用方法:


    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();
        }
    }