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

如何在C#中清除事件订阅?,c#,.net,events,delegates,C#,.net,Events,Delegates,参加以下C#类课程: 如果有大量订阅c1的someEvent事件,我想全部清除它们,那么实现这一点的最佳方法是什么也可以认为对该事件的订阅可以是lambdas/匿名委托。< /强> 目前我的解决方案是向c1添加一个ResetSubscriptions()方法,该方法将someEvent设置为null。我不知道这是否有任何看不见的后果。向c1添加一个将“someEvent”设置为null的方法 public class c1 { event EventHandler someEvent;

参加以下C#类课程:

如果有大量订阅
c1
someEvent
事件,我想全部清除它们,那么实现这一点的最佳方法是什么<强>也可以认为对该事件的订阅可以是lambdas/匿名委托。< /强>


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

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

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

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

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

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

值得注意的是类字段事件实际上在做什么——它们同时创建变量和事件。在类中,您最终会引用变量。从外部引用事件


有关详细信息,请参阅my。

在类内将事件设置为null是有效的。当您处置一个类时,您应该始终将事件设置为null,GC会遇到事件问题,如果处置的类有悬空事件,则可能无法清理该类。

注释。

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

WM_Paint=998;// 清除所有订阅服务器的最佳实践是,如果要将此功能公开给外部,请通过添加另一个公共方法将someEvent设置为null。这没有看不见的后果。先决条件是记住用关键字“event”声明SomeEvent

请参阅《简而言之,C#4.0》一书,第125页

这里有人建议使用
Delegate.RemoveAll
方法。如果您使用它,示例代码可以遵循以下表单。但这真的很愚蠢。为什么不在
ClearSubscribers()
函数中使用
SomeEvent=null

public void ClearSubscribers ()
{
   SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
   // Then you will find SomeEvent is set to null.
}
删除所有事件,假设事件为“操作”类型: 最好使用
delegate{}
而不是
null
来避免null ref异常。

这是我的解决方案:

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()){/*…*/}
模式来取消订阅调用列表的所有成员。

而不是手动添加和删除回调,并在任何地方声明一堆委托类型:

// 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();
    }
}
公共类对象
{
公共广播m_EventToBroadcast=新广播();
void SetupListener()
{
m_EventToBroadcast.SubscribeOnce((ObjectType广播器)=>{
//这里的一次性逻辑
});
}
~Object()
{
m_EventToBroadcast.Dispose();
m_EventToBroadcast=null;
}
void广播事件()
{
m_EventToBroadcast.广播(本);
}
}
公众代表(T广播公司);
公共类广播:IDisposable
{
私有事件ObjectDelegate m_事件;
私有列表m_SingleSubscribers=新列表();
~Broadcast()
{
处置();
}
公共空间处置()
{
清除();
System.GC.SuppressFinalize(本);
}
公共空间清除()
{
m_SingleSubscribers.Clear();
m_事件=委托{};
}
//向该代理添加一个在第一次广播后删除的快照
public void SubscribeOnce(ObjectDelegate del)
{
m_事件+=del;
m_SingleSubscribers.Add(del);
}
//添加每次调用的循环委托
公共无效订阅(ObjectDelegate)
{
m_事件+=del;
}
公共无效取消订阅(ObjectDelegate)
{
m_事件-=del;
}
公共广播(T广播公司)
{
m_事件?调用(广播方);
对于(int i=0;i
这就是我看到的行为。正如我在问题中所说的,我不知道我是否忽略了什么。我不相信这对lambda表达式或匿名代理有效。如果你很固执,你可以通过反思来强制澄清。请参阅。@Brian:这取决于实现。如果它只是一个类似于event或
EventHandlerList
的字段,则您可能能够。不过,您必须识别这两种情况,而且可能有任意数量的其他实现。@Joshua:不,它会将变量的值设置为null。我同意变量不会被称为
hidden
@JonSkeet这就是我(认为)我说的。它的编写方式让我困惑了5分钟。@JoshuaLamusga:你说它会清除调用列表,听起来像是在修改现有对象。如果你在声明事件的类型中,你不需要这样做,你可以将它设置为null,如果您在类型之外,则无法获取委托的调用列表。另外,如果调用
GetInvocationList
时事件为null,代码会抛出异常。为什么?你能详细介绍一下这个答案吗?@S.Buda,因为如果它是空的,那么你会得到一个空的引用。这就像使用
List.Clear()
vs
myList=null
。你能格式化你的问题并删除左边的所有空白吗?当您从IDE复制和粘贴时,可能会发生这种情况。只要去掉空白,my badDelegate.RemoveAll对MulticastDelegate有效:public class Foo : IDisposable { private event EventHandler _statusChanged; public event EventHandler StatusChanged { add { _statusChanged += value; } remove { _statusChanged -= value; } } public void Dispose() { _statusChanged = null; } }
// 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();
    }
}