Должен ли я отписаться от анонимных обработчиков событий локальных переменных? - PullRequest
11 голосов
/ 22 февраля 2011

Если у меня есть код, похожий на этот:утечка памяти из-за ссылки на SomeEvent?

Ответы [ 3 ]

18 голосов
/ 22 февраля 2011

Ваша ситуация в порядке; событие подписчик не помешает сбору издателя , но может произойти обратное.

Например,

class Foo
{
    public event EventHandler FooEvent;

    void LeakMemory()
    {
        Bar bar = new Bar();

        bar.AttachEvent(this);
    }
}

class Bar
{
    void AttachEvent(Foo foo)
    {
        foo.FooEvent += (sender, e) => { };
    }
}

В этом случае экземпляр Bar, созданный в LeakMemory, не может быть собран до

  • Анонимный метод, представленный лямбда-выражением, удален из списка вызовов FooEvent
  • Экземпляр Foo, к которому он прикреплен, может быть собран

Это потому, что событие (которое является просто некоторым синтаксическим сахаром по сравнению с обычным delegate экземпляром) хранится в списке делегатов, которые нужно вызвать при его вызове, и каждый из этих делегатов, в свою очередь, имеет ссылку на объект к которому он прикреплен (в данном случае, экземпляр Bar).

Обратите внимание, что речь идет только о коллекции eligibility здесь. Просто потому, что он имеет право, ничего не говорится о , когда (или даже, на самом деле, , если ) он будет собран, просто о том, что может быть.

1 голос
/ 22 февраля 2011

Ответы выше верны; Я просто хотел сделать заметку. Анонимные делегаты, используемые в качестве обработчиков, могут быть отписаны только в том случае, если вы сохраните какую-либо другую ссылку на делегат / лямбду. Это связано с тем, что лямбда-выражения являются «функциональными литералами», вроде строковых литералов, однако в отличие от строк они НЕ сравниваются семантически при определении равенства:

public event EventHandler MyEvent;

...

//adds a reference to this named method in the context of the current instance
MyEvent += Foo;

//Adds a reference to this anonymous function literal to MyEvent
MyEvent += (s,e) => Bar();

...

//The named method of the current instance will be the same reference
//as the named method.
MyEvent -= Foo;

//HOWEVER, even though this lambda is semantically equal to the anonymous handler, 
//it is a different function literal and therefore a different reference,
//which will not match the anonymous handler.
MyEvent -= (s,e) => Bar();

var hasNoHandlers = MyEvent == null; //false

//To successfully unsubscribe a lambda, you have to keep a reference handy:

EventHandler myHandler = (s,e) => Bar();

MyEvent += myHandler;

...

//the variable holds the same reference we added to the event earlier,
//so THIS call will remove the handler.
MyEvent -= myHandler;
1 голос
/ 22 февраля 2011

Ну, объект bar ссылается не будет автоматически собирать мусор сразу ... просто переменная bar не будет предотвращать его сборку мусора.

Обработчик событий не помешает также сборке мусора экземпляра Bar - "нормальная" проблема заключается в том, что обработчик событий удерживает подписчика события от сбора мусора ( если он использует метод экземпляра или фиксирует «this» в анонимной функции). Обычно это не влияет на сборщик мусора publisher . Просто помните, что издателю необходимо сохранить ссылку на всех подписчиков - подписчику не нужно помнить, на что он подписан, если только он явно не хочет отписаться или использовать какого-либо другого участника позже.

Предполагая, что ничто другое не поддерживает ваш экземпляр Bar, ваш код должен быть в порядке.

...