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

采用下面的 C # 类:

c1 {
event EventHandler someEvent;
}

如果有很多订阅 c1someEvent事件,我想清除他们所有,什么是最好的方式来实现这一点?还要考虑对此事件的订阅可以是/are lambdas/匿名委托。

目前,我的解决方案是向 c1添加一个 ResetSubscriptions()方法,将 someEvent设置为 null。我不知道这会不会有什么不可预见的后果。

116525 次浏览

向 c1中添加一个方法,该方法将“ some Event”设置为 null。

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

可以通过使用授权。移除或授权。移除所有方法来实现此目的。

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

从类外部,你不能这样做-事件基本上暴露“订阅”和“取消订阅”,就是这样。

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

有关详细信息,请参阅我的 关于活动和代表的文章

在类中将事件设置为 null 可以工作。当您释放一个类时,您应该始终将事件设置为 null,GC 在事件方面有问题,并且如果有悬挂事件,可能无法清除释放的类。

概念扩展无聊评论。

我宁愿使用“事件处理程序”这个词,而不是“事件”或“委托”。还用“事件”这个词来形容其他的东西。在某些编程语言(VB.NET、 Object Pascal、 Objective-C)中,“ event”被称为“ message”或“ sign”,甚至还有“ message”关键字和特定的 Sugar 语法。

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;

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

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

清除所有订阅者的最佳实践是,如果希望向外部公开此功能,可以通过添加另一个公共方法将 somEvent 设置为 null。这不会产生看不见的后果。前提条件是记住使用关键字“ event”声明 Some Event。

请看这本书-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.
}

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

Delegate[] dary = TermCheckScore.GetInvocationList();


if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
class c1
{
event EventHandler someEvent;
ResetSubscriptions() => someEvent = delegate { };
}

为了避免 null ref 异常,使用 delegate { }比使用 null更好。

这是我的解决办法:

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