Какая модель С# имеет лучшую производительность, чтобы избежать дублирования обработчиков событий?
В принципе существует два шаблона, позволяющих избежать дублирования регистрации обработчиков событий:
(В соответствии с этим обсуждением: шаблон С#, чтобы предотвратить перехват обработчика событий)
-
Используя пространство имен System.Linq и проверьте, зарегистрирован ли обработчик события, вызывая GetInvocationList().Contains(MyEventHandlerMethod);
-
Выполняйте регистрацию перед регистрацией, например:
MyEvent -= MyEventHandlerMethod;
MyEvent += MyEventHandlerMethod;
Мой вопрос в том, насколько эффективен, какой из них лучше, или существует существенная разница между ними в производительности?
Ответы
Ответ 1
Согласно документации, список вызовов хранится как массив или что-то похожее на него, а порядок обработчиков событий также сохраняется, Может быть, есть внутренняя структура, чтобы поддерживать быстрый поиск конкретного метода там.
Итак, в худшем случае операция GetInvocationList().Contains(MyEventHandlerMethod);
равна O(1)
(поскольку мы просто получили ссылку для массива) + O(n)
для поиска метода, даже если для него нет оптимизации. Я серьезно сомневаюсь, что это правда, и я думаю, что есть некоторый оптимизирующий код, и это O(log_n)
.
Второй подход имеет дополнительную операцию добавления, которая, я думаю, O(1)
, поскольку мы добавляем обработчик событий в конец.
Итак, чтобы увидеть разницу между такими действиями, вам нужно много обработчиков событий.
Но! Если вы используете подход второй, как я уже сказал, , вы добавите обработчик событий в конец очереди, что может быть ошибочным в некоторых случаях. Поэтому используйте первую и не сомневайтесь в ней.
Ответ 2
Я не думаю, что это имеет большое значение, как в предполагаемой производительности, так и в фактической разнице.
Оба GetInvocationList
и -=
проходят внутренний массив _invocationList
. (См. source)
Метод расширения LINQ Contains
займет больше времени, так как ему нужен весь массив, который нужно пройти и преобразовать, вернуть, а затем проверить самим Contains
. Преимущество Contains
заключается в том, что не нужно добавлять обработчик событий, если он существует, что будет означать некоторое увеличение производительности.
Ответ 3
- не будет работать для внешних абонентов и не очень эффективен в любом случае
- должно быть хорошо (обратите внимание, что каждый раз создается 2 экземпляра делегата), однако также рассмотрите
- в большинстве сценариев, вам должно быть легко узнать, подписаны ли вы; если вы не можете знать, то это предполагает архитектурную проблему.
Типичным использованием будет "подписаться на {some usage} [unsubscribe]", где отмена подписки может не понадобиться, в зависимости от относительной продолжительности жизни издателя событий и подписчика; если у вас действительно есть сценарий повторного входа, то "подписаться, если он еще не подписан" сам по себе проблематичен, потому что, когда вы не подписываетесь позже, вы не знаете, если вы предотвращаете внешнюю итерацию, получающую событие.
Ответ 4
MyEvent -= MyEventHandlerMethod
сначала нужно найти зарегистрированный обработчик событий в списке вызовов, чтобы удалить его.
Так что GetInvocationList().Contains
лучше, но это действительно несущественно.
Но обратите внимание, что вы не можете получить доступ к списку вызовов event EventHandler foo
....