Почему и как избежать утечек памяти обработчика событий?

Я только понял, прочитав несколько вопросов и ответов на StackOverflow, что добавление обработчиков событий с использованием += в С# (или, я думаю, других языков .net) может вызвать общие утечки памяти...

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

Как это работает (что означает, почему это действительно вызывает утечку памяти)?
Как я могу исправить эту проблему? Использует ли -= для одного обработчика событий?
Существуют ли общие шаблоны проектирования или лучшие практики для обработки подобных ситуаций?
Пример. Как я должен обрабатывать приложение с множеством разных потоков, используя множество разных обработчиков событий для создания нескольких событий в пользовательском интерфейсе?

Есть ли какие-либо хорошие и простые способы эффективно отслеживать это в уже построенном большом приложении?

Ответ 1

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

Если издатель живет дольше, чем абонент, то он будет поддерживать подписчика в живых, даже если нет других ссылок на подписчика.

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

Это возможная причина... но по моему опыту она скорее чрезмерно раздута. Ваш пробег может варьироваться, конечно... вам просто нужно быть осторожным.

Ответ 2

Да, -= достаточно, однако, может быть довольно сложно отслеживать каждое назначенное событие. (подробно, см. пост Джона). Что касается шаблона проектирования, посмотрите слабый шаблон события.

Ответ 3

Событие - действительно связанный список обработчиков событий

Когда вы делаете + = новый EventHandler в событии, на самом деле не имеет значения, добавлена ​​ли эта конкретная функция в качестве слушателя раньше, он будет добавлен один раз за + =.

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

См. здесь

и MSDN ЗДЕСЬ