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

后端 未结 5 1586
滥情空心
滥情空心 2020-12-08 07:04

Basic premise:

I have a Room which publishes an event when an Avatar \"enters\" to all Avatars within the Room. When an Avatar leaves the Room I want it to remove a

相关标签:
5条回答
  • 2020-12-08 07:21

    Probably the simplest way to accomplish this would be to store all of your subscribed events for an avatar in an ArrayList of delegates to the events.

    When the avatar leaves the room, simply loop through the list of delegates performing a standard remove (-=).

    0 讨论(0)
  • 2020-12-08 07:22

    Is there anything wrong with a standard remove?

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

    EDIT

    Based on your update it appears that you want code that will remove a particular object from all events on a particular class. There is no realistic way to accomplish this goal. It's often a bit verbose but the best way is to individually add/remove a particular object method combo from every event.

    The only way to get close to this functionality is to use reflection. You could reflectively grab all events on your class and then do some magic to find all instances of a class within the event chain. This will only be a partial solution though because it will ignore such things as a lambda expression event handlers.

    0 讨论(0)
  • 2020-12-08 07:31

    you can run on all the event subscribers with:

    _Event.GetInvocationList()
    

    and remove each event handler.

    Delegate[] subscribers = myEvent.GetInvocationList();
    
    for(int i = 0; i < subscribers.Length; i++)    
    {    
        myEvent -= subscribers[i] as yourDelegateType;   
    }
    
    0 讨论(0)
  • 2020-12-08 07:36

    Each delegate has a method named GetInvocationList() that returns all the actual delegates that have been registered. So, assuming the delegate Type (or event) is named say MyDelegate, and the handler instance variable is named myDlgHandler, you can write:

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

    to cover the case where it might be null,

     if(myDlgHandler != null)
      foreach (var d in myDlgHandler.GetInvocationList())
           myDlgHandler -= (d as MyDelegate);
    
    0 讨论(0)
  • 2020-12-08 07:36

    What I'd like to do is in debug (do not think that this is good performance wise for release and one should catch it during development) throw exceptions when a class's events are not unsubscribed, this is the method that I use:

    #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
    

    The in my Dispose of the item that owns the delegate, or any other location where you're workflow supposed to release the object I would call it like this.

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

    It's then super easy to find the subscribers to these "orphaned" events and fix the code

    ps: An Extension of this "practice pattern" looks like this.

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

    }

    0 讨论(0)
提交回复
热议问题