繁体   English   中英

如何取消订阅C#中特定类的事件的所有处理程序?

[英]How do I unsubscribe all handlers from an event for a particular class in C#?

基本前提:

我有一个房间,当一个阿凡达“进入”房间内的所有头像时,它会发布一个事件。 当阿凡达离开房间时,我希望它删除该房间的所有订阅。

在将“阿凡达”添加到新房间并订阅新房间的活动之前,我怎样才能最好地取消订阅房间中所有活动的阿凡达?

代码如下:

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

每个委托都有一个名为GetInvocationList()的方法,它返回已注册的所有实际委托。 因此,假设委托类型(或事件)被命名为MyDelegate ,并且处理程序实例变量名为myDlgHandler ,您可以编写:

Delegate[] clientList = myDlgHandler.GetInvocationList();
foreach (var d in clientList)
       myDlgHandler -= (d as MyDelegate);

覆盖它可能为null的情况,

 if(myDlgHandler != null)
  foreach (var d in myDlgHandler.GetInvocationList())
       myDlgHandler -= (d as MyDelegate);

标准删除有什么问题吗?

public void RemovePlayer(Avatar theAvatar) {
 AvatarEntersRoom -= new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);

}

编辑

根据您的更新,您似乎希望代码能够从特定类的所有事件中删除特定对象。 没有现实的方法来实现这一目标。 它通常有点冗长,但最好的方法是从每个事件中单独添加/删除特定的对象方法组合。

接近此功能的唯一方法是使用反射。 你可以反思地抓住你班上的所有事件,然后做一些魔术来找到事件链中所有类的实例。 这只是一个部分解决方案,因为它会忽略诸如lambda表达式事件处理程序之类的东西。

实现此目的的最简单方法可能是将所有订阅的事件存储在事件的委托的ArrayList中。

当化身离开房间时,只需循环执行标准删除( -= )的代表列表。

您可以在所有活动订阅者上运行:

_Event.GetInvocationList()

并删除每个事件处理程序。

Delegate[] subscribers = myEvent.GetInvocationList();

for(int i = 0; i < subscribers.Length; i++)    
{    
    myEvent -= subscribers[i] as yourDelegateType;   
}

我想要做的是在调试中(不要认为这对于发布是明智的,并且应该在开发期间捕获它)当类的事件没有取消订阅时抛出异常,这是我使用的方法:

#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

在我的Dispose中拥有委托的项目,或者你工作流程应该释放对象的任何其他位置,我会这样称呼它。

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

然后,很容易找到这些“孤立”事件的订阅者并修复代码

ps:这种“练习模式”的扩展看起来像这样。

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}]");
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM