如何取消订阅C#中特定类事件的所有处理程序?
基本前提: 我有一个房间,当一个化身“进入”房间内的所有化身时,它会发布一个事件。当化身离开房间时,我希望它删除该房间的所有订阅 在我将头像添加到新房间并订阅新房间的活动之前,如何最好地从房间中的所有活动中取消订阅头像 代码是这样的:如何取消订阅C#中特定类事件的所有处理程序?,c#,.net,events,C#,.net,Events,基本前提: 我有一个房间,当一个化身“进入”房间内的所有化身时,它会发布一个事件。当化身离开房间时,我希望它删除该房间的所有订阅 在我将头像添加到新房间并订阅新房间的活动之前,如何最好地从房间中的所有活动中取消订阅头像 代码是这样的: class Room { public event EventHandler<EnterRoomEventArgs> AvatarEntersRoom; public event EvnetHandler<LeaveRoomEvent
class Room
{
public event EventHandler<EnterRoomEventArgs> AvatarEntersRoom;
public event EvnetHandler<LeaveRoomEventArgs> AvatarLeavesRoom;
public event EventHandler<AnotherOfManyEventArgs> AnotherOfManayAvatarEvents;
public void AddPlayer(Avatar theAvatar)
{
AvatarEntersRoom(this, new EnterRoomEventArgs());
AvatarEntersRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AvatarLeavesRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AnotherOfManayAvatarEvents += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
}
class Avatar
{
public void HandleAvatarEntersRoom(object sender, EnterRoomEventArgs e)
{
Log.Write("avatar has entered the room");
}
public void HandleAvatarLeaveRoom(object sender, LeaveRoomEventArgs e)
{
Log.Write("avatar has left room");
}
public void HandleAnotherOfManayAvatarEvents(object sender, AnotherOfManyEventArgs e)
{
Log.Write("another avatar event has occurred");
}
}
教室
{
公共事件处理人/租赁室;
公共事件EvnetHandler AvatarLeavesRoom;
公共事件处理者——另一个Manayavatare事件;
公共虚空添加玩家(阿凡达)
{
AvatarEntersRoom(这是新的EnterRoomEventArgs());
AvatarEntersRoom+=新事件处理程序(theAvatar.HandleAvatarEntersRoom);
AvatarLeavesRoom+=新事件处理程序(theAvatar.HandleAvatarEntersRoom);
另一个manayavatarents+=新事件处理程序(theAvatar.HandleAvatarEntersRoom);
}
}
类化身
{
public void HandleAvatarEntersRoom(对象发送方,EnterRoomEventArgs e)
{
Log.Write(“阿凡达进入房间”);
}
public void HandleAvatarLeaveRoom(对象发送者、LeaveRoomEventArgs e)
{
写(“阿凡达已经离开房间”);
}
MANAYAVATAREvents(对象发送方,另一个多事件参数e)的public void handleanothers
{
Log.Write(“发生了另一个化身事件”);
}
}
标准删除有什么问题吗
public void RemovePlayer(Avatar theAvatar) {
AvatarEntersRoom -= new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
public void RemovePlayer(化身theAvatar){
AvatarEntersRoom-=新事件处理程序(theAvatar.HandleAvatarEntersRoom);
}
编辑
根据您的更新,您似乎需要从特定类的所有事件中删除特定对象的代码。实现这一目标没有现实的方法。这通常有点冗长,但最好的方法是在每个事件中单独添加/删除特定的对象方法组合
接近此功能的唯一方法是使用反射。您可以反射地抓取类上的所有事件,然后执行一些魔术来查找事件链中某个类的所有实例。不过,这只是部分解决方案,因为它将忽略lambda表达式事件处理程序等内容 实现这一点的最简单方法可能是将所有订阅的头像事件存储在事件代理的
ArrayList
中
当化身离开房间时,只需在执行标准移除的代理列表中循环(
-=
)。每个代理都有一个名为GetInvocationList()
的方法,该方法返回所有已注册的实际代理。因此,假设委托类型(或事件)名为sayMyDelegate
,处理程序实例变量名为myDlgHandler
,则可以编写:
Delegate[] clientList = myDlgHandler.GetInvocationList();
foreach (var d in clientList)
myDlgHandler -= (d as MyDelegate);
为了涵盖可能为空的情况
if(myDlgHandler != null)
foreach (var d in myDlgHandler.GetInvocationList())
myDlgHandler -= (d as MyDelegate);
您可以通过以下方式在所有事件订阅服务器上运行:
_Event.GetInvocationList()
并删除每个事件处理程序
Delegate[] subscribers = myEvent.GetInvocationList();
for(int i = 0; i < subscribers.Length; i++)
{
myEvent -= subscribers[i] as yourDelegateType;
}
Delegate[]subscribers=myEvent.GetInvocationList();
对于(int i=0;i
我想做的是在调试中(不要认为这对于发布来说是很好的性能,在开发过程中应该捕捉到它)在类的事件未被取消订阅时抛出异常,这是我使用的方法:
#if DEBUG
private void CheckEventHasNoSubscribers(Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// throw an exception listing all current subscription that would hinder GC on them!
throw new Exception(
$"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}
#endif
在“我的处置”中,将处置拥有该委托的项,或者您应该在其中释放该对象的任何其他位置,我将这样称呼它
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (_orderCacheLock != null)
_orderCacheLock.Dispose();
if(_SettingTradeTimeOut!=null)
_SettingTradeTimeOut.Dispose();
_orderCacheLock = null;
#if DEBUG
CheckEventHasNoSubscribers(OnIsProfitable);
CheckEventHasNoSubscribers(OnPropertyChanged);
#endif
disposedValue = true;
}
}
public static void CheckEventHasNoSubscribers(this Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// point to the missing un-subscribed events
throw new Exception( $"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
然后就可以非常容易地找到这些“孤立”事件的订户并修复代码
附言:
这种“实践模式”的扩展如下所示
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (_orderCacheLock != null)
_orderCacheLock.Dispose();
if(_SettingTradeTimeOut!=null)
_SettingTradeTimeOut.Dispose();
_orderCacheLock = null;
#if DEBUG
CheckEventHasNoSubscribers(OnIsProfitable);
CheckEventHasNoSubscribers(OnPropertyChanged);
#endif
disposedValue = true;
}
}
public static void CheckEventHasNoSubscribers(this Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// point to the missing un-subscribed events
throw new Exception( $"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}正确,但框架是否将代理存储在某种类型的列表中的某个位置?对此不确定。。。您在考虑多播委托吗?是的,在委托本身中,每个委托都继承自System.MultiCastDelegate,并包含任意数量的委托的内部存储。当你“向一个委托注册一个delegateHandler时,它只是将新的Hndlr添加到列表中。我不认为这个例子是完整的,这个问题涉及多个事件。搜索的答案是神奇的“删除我拥有的任何附加到你的任何事件的委托”“@Anthony,提问者在我回答后更新了问题。我将更新我的回答您的代码示例使您的问题变得模糊。最重要的词是问题“事件”中的最后一个词,尤其是它的复数形式。代码示例应该显示多个事件,并且您希望轻松地从所有事件中注销。这是否回答了您的问题?最新的表单实际上可能会崩溃。如果
myDlgHandler
为null,则foreach
将计算为foreach(null中的vard)
,这将抛出NullReferenceException
。请说明此实现比简单地将myDlgHandler
设置为null
更优越。最终的结果是一样的;变量的值为null
。那么,为什么要浪费所有的代码和时间来调用GetInvocationList()
和循环呢?(不要介意崩溃bug。)@Peter,这种方法允许代码支持有条件地删除一些委托,并将父委托保留在适当的位置,以便分配其他不同的委托。在这种情况下(OPs场景),他希望化身进入另一个房间。因此,代表将立即用于另一个不同的房间。甚至可以编写代码来添加新房间的事件,然后删除与当前占用房间不同的所有房间的事件。“这种方法允许代码支持有条件地删除一些代理”——基于什么标准?代码示例中没有那么糟糕的地方