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

В каких случаях требуется отстранение от необходимых событий?

Я не уверен, полностью ли я понимаю последствия прикрепления к событиям в объектах.

Это мое настоящее понимание, правильное или подробное:

1. Привязка к локальным событиям класса не требуется отсоединять

Примеры:

this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

public event EventHandler OnMyCustomEvent = delegate { };

Я предполагаю, что когда ваш объект удаляется или мусор собирается, функции освобождаются и автоматически отделяются от событий.

2. Прикрепление к объектам, которые вам больше не нужны (= null;), необходимо отделить от

Примеры: Присоединение к таймеру Истекшее событие, на которое вы реагируете только один раз. Я предполагаю, что вам нужно хранить таймер в локальной переменной, чтобы вы могли отключить событие "Истекшее" после возникновения события. Таким образом, объявление таймера в области локального метода, например, приведет к утечке:

System.Timers.Timer myDataTimer = new System.Timers.Timer(1000); myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);

3. Прикрепление к событиям в локальном объекте к вашему классу не требует удаления?

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

Я уверен, что у меня есть места, где я прекратил использовать объекты и не смог отсоединиться от события (например, пример таймера, который я сделал), поэтому я ищу более четкое объяснение того, как это работает.

4b9b3361

Ответ 1

Я думаю, вы делаете это более сложным, чем нужно. Вам просто нужно запомнить две вещи:

  • Когда вы подписываетесь на событие, событие "владелец" (издатель) обычно содержит ссылку на делегата, с которым вы подписаны.
  • Если вы используете метод экземпляра в качестве действия делегата, то делегат имеет ссылку на его "целевой" объект.

Это означает, что если вы пишете:

publisher.SomeEvent += subscriber.SomeMethod;

Тогда subscriber не будет иметь права на сбор мусора до publisher, если вы не отмените подписку позже.

Обратите внимание, что во многих случаях subscriber является просто this:

publisher.SomeEvent += myDataTimer_Elapsed;

эквивалентно:

publisher.SomeEvent += this.myDataTimer_Elapsed;

считая его методом экземпляра.

Нет обратной связи только из-за подписки на события - другими словами, абонент не сохраняет живого издателя.

См. мою статью о событиях и делегатах для получения дополнительной информации, кстати.

Ответ 2

Остальные ссылки, препятствующие сборке мусора, имеют еще один эффект, который может быть очевиден, но, тем не менее, еще не указан в этой теме; подключенный обработчик событий также будет исключен.

Я испытал это несколько раз. Один из них заключался в том, что у нас было приложение, которое постепенно становилось медленнее и медленнее, чем дольше работало. Приложение создало пользовательский интерфейс динамически, загружая пользовательские элементы управления. Контейнер заставил пользовательские элементы управления подписываться на определенные события в среде, и одна из них не была отписана, когда элементы управления были "разгружены".

Через некоторое время это привело к тому, что большое количество прослушивателей событий выполнялось каждый раз, когда возникало конкретное событие. Это, конечно, может привести к серьезным условиям гонки, когда большое количество "спящих" случаев внезапно пробуждается и пытается действовать на одном и том же входе.

Короче говоря; если вы пишете код, чтобы подключить прослушиватель событий; убедитесь, что вы отпустите, как только это не понадобится. Я почти осмеливаюсь пообещать, что в какой-то момент в будущем он спасет вас от хотя бы одной головной боли.

Ответ 3

Соответствующий случай, когда вы должны отказаться от подписки на событие, выглядит следующим образом:

public class A
{
    // ...
    public event EventHandler SomethingHappened;
}

public class B
{
    private void DoSomething() { /* ... */ } // instance method

    private void Attach(A obj)
    {
       obj.SomethingHappened += DoSomething();
    }
}

В этом случае, когда вы удаляете B, по-прежнему будет ссылка на него из обработчика события obj. Если вы хотите восстановить память B, вам нужно сначала отсоединить B.DoSomething() от соответствующего обработчика событий.

Вы можете столкнуться с тем же, если бы линия подписки на события выглядела так:

obj.SomethingHappened += someOtherObject.Whatever.DoSomething();

Теперь он someOtherObject, что на крючке и не может быть собрано мусор.