Работа с обработчиками событий в .NET - PullRequest
3 голосов
/ 17 февраля 2011

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

Мне интересно вот эти вещи:

  • Есть ли способ посмотреть, какие обработчики были добавлены к событию объекта?
  • Можно ли удалить все обработчики из события?
  • Где хранятся эти корреляции между событием и его обработчиком?

Ответы [ 2 ]

2 голосов
/ 17 февраля 2011

Если событие помечено ключевым словом C # event , то снаружи объекта нет возможности увидеть подписчиков - необходимая информация просто не видна.

Изнутри это можно сделать, хотя это сложно и зависит от деталей реализации, которые могут измениться (хотя они еще не изменились).

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

Итак, этот код действителен:

myConnection.Closing -= ConnectionClosingHandler;
myConnection.Closing += ConnectionClosingHandler;

Если вы уже подписаны на событие, первая строка удаляет подписку.
Если вы не уже подписаны на событие, первая строка ничего не делает.

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

Чтобы ответить на последний пункт, когда вы объявляете нормальное событие:

public event PropertyChangedEventHandler Changed;

Компилятор создает переменную-член типа PropertyChangedEventHandler, в которой хранятся все подписчики. Вы можете взять на себя хранение, если хотите:

public event PropertyChangedEventHandler Changed
{ 
    add { ... }
    remove { ... }
}

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

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

Корреляции между событием и его обработчиками сохраняются в самом событии.Когда вы получаете доступ к событию, эта информация фактически копируется в группу методов.Вот почему вы должны сказать:

var onclick = Click;
if (onclick != null) onclick();

Если бы я обращался к событию Click дважды, а не к промежуточной переменной onclick, я бы вызвал копирование любых событий дважды.Кроме того, в многопоточном сценарии, если кто-то удалил обработчик между проверкой Click != null и вызовом обработчика, я мог бы в конечном итоге вызвать исключение.

Если вы уже знаете, какой обработчик вы хотите удалить, онэтот обработчик легко удалить:

EventHandler handler1 = (sender, e) => Console.WriteLine("test");
Click += handler1;
Click -= handler1;

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

foreach(var handler in Click.GetInvocationList())
    Console.WriteLine(handler.Method.ToString());

Однако информация, которую вы получаете, находится в форме объекта Delegate.Он может быть вызван (что может быть полезно, если вы хотите перехватить любые исключения, генерируемые одним обработчиком, и продолжить вызывать остальные обработчики), но C # не предоставляет простой способ удалить обработчик из события, основываясь исключительно на этой информации.,Некоторые ответы на Как удалить все обработчики событий из элемента управления , кажется, указывают на то, что вы можете использовать Reflection для этого или использовать команду RemoveHandler Visual Basic.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...