Различия между делегатами и событиями

Предыдущий

Разработчики, не имеющие опыта работы с платформой .NET Core, часто не могут решить, что следует выбрать: структуру на основе delegates или на основе events. Часто выбор делегатов или событий довольно сложен, так как эти две возможности языка довольно похожи. Более того, события основаны на тех же средствах языка, которые обеспечивают поддержку делегатов.

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

Учитывая такое сходство, легко могут возникнуть проблемы с выбором подходящего механизма в той или иной ситуации.

Прослушивание событий необязательно

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

Обратите внимание на примеры, рассматривавшиеся в этом разделе. Код, который вы создавали с помощью List.Sort(), должен содержать функцию сравнения для правильной сортировки элементов. Запросам LINQ необходимо предоставить делегаты для определения элементов, которые следует вернуть. В обоих случаях использовалась структура на основе делегатов.

Рассмотрим событие Progress. Оно сообщает о ходе выполнения задачи. Задача продолжает выполняться вне зависимости от того, есть ли прослушиватели. Еще один пример — FileSearcher. Нахождение всех искомых файлов производится, даже если не подключены обработчики событий. Элементы управления UX продолжают работать правильно, даже если нет подписчиков, прослушивающих события. В обоих примерах используются структуры на основе событий.

Для возвращаемых значений требуются делегаты

Еще одним аспектом является прототип метода, который требуется для метода делегата. Как вы уже видели, все делегаты, используемые для событий, имеют тип возвращаемого значения void. Вы уже также знаете, что есть идиомы для создания обработчиков событий, которые передают информацию обратно источникам событий, изменяя свойства объекта аргумента события. Хотя такие идиомы работают, они не так естественны, как возвращение значения из метода.

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

Для событий используется закрытый вызов

Классы, отличные от тех, в которых содержится событие, могут только добавлять и удалять прослушиватели событий. Вызывать событие может только класс, содержащий событие. События обычно являются открытыми членами класса. Для сравнения, делегаты часто передаются как параметры и хранятся как закрытые члены класса (если хранятся вообще).

Прослушиватели событий часто имеют более длительный срок существования

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

Сравните это с часто встречающимися структурами на основе делегатов, когда делегат применяется в качестве аргумента метода и не используется после того, как метод возвращает управление.

Тщательно оценивайте ситуацию

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