Подтвердить что ты не робот

Как отказаться от всех обработчиков из события для определенного класса в С#?

Основная предпосылка:

У меня есть Комната, в которой публикуется событие, когда Аватар "входит" ко всем Аватарам в Комнате. Когда Аватар покидает комнату, я хочу, чтобы он удалил все подписки для этой комнаты.

Как я могу лучше всего отменить аватар из всех событий в комнате, прежде чем добавить Аватар в новую комнату и подписаться на новые события Room?

Код выглядит примерно так:

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

Ответ 1

Каждый делегат имеет метод с именем GetInvocationList(), который возвращает все фактические зарегистрированные делегаты. Итак, если предположить, что тип (или событие) делегата имеет имя say MyDelegate, а переменная экземпляра обработчика называется 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);

Ответ 2

Что-то не так со стандартным удалением?

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

}

ИЗМЕНИТЬ

Основываясь на вашем обновлении, вам кажется, что вам нужен код, который удалит определенный объект из всех событий в определенном классе. Нет реалистичного способа достижения этой цели. Это часто немного подробный, но наилучшим способом является индивидуальное добавление/удаление конкретного метода метода объекта из каждого события.

Единственный способ получить закрыть для этой функции - использовать отражение. Вы могли бы отразить все события в своем классе, а затем сделать магию, чтобы найти все экземпляры класса в цепочке событий. Это будет лишь частичным решением, потому что оно будет игнорировать такие вещи, как обработчики событий выражения лямбда.

Ответ 3

Вероятно, самым простым способом добиться этого было бы сохранить все ваши подписанные события для аватара в ArrayList делегатов для событий.

Когда аватар покидает комнату, просто прокрутите список делегатов, выполняющих стандартный remove (-=).

Ответ 4

вы можете запускать всех подписчиков событий с помощью:

_Event.GetInvocationList()

и удалите каждый обработчик событий.

Delegate[] subscribers = myEvent.GetInvocationList();

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

Ответ 5

То, что я хотел бы сделать, - это отладка (не думайте, что это хорошая производительность для релиза, и ее нужно ловить во время разработки), генерировать исключения, когда события класса не отписываются, это метод, который я использую:

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

}