Общие сведения о событиях
События, так же как и делегаты, представляют собой механизм позднего связывания. На самом деле события основаны на тех же средствах языка, которые обеспечивают поддержку делегатов.
С помощью событий объект может сообщить всем компонентам системы, которым это необходимо, о том, что что-то произошло. Любой другой компонент может подписаться на событие, чтобы получать уведомления о его наступлении.
Возможно, вы уже пользовались событиями при программировании. Во многих графических системах есть модель событий, которая позволяет сообщать о действиях пользователей. Такие события сообщают о перемещениях мыши, нажатиях кнопок и иных подобных действиях. Это наиболее распространенный, но, безусловно, не единственный вариант использования событий.
Вы можете определить события, которые должны вызываться для классов. Важным моментом при работе с событиями является то, что для определенного события может быть не зарегистрирован ни один объект. Код необходимо писать так, чтобы он не вызывал событий, если прослушиватели не настроены.
При подписке на событие также создается взаимосвязь между двумя объектами (источником события и приемником событий). Если приемник событий больше не должен получать события, необходимо отменить его подписку на источник события.
Цели при проектировании поддержки событий
Ниже перечислены цели модели событий, реализуемой в языке.
Между источником события и приемником событий должна быть минимальная взаимосвязь. Эти два компонента могут создаваться разными организациями и даже обновляться по совершенно разным графикам.
Подписка на событие и отмена подписки на него должны производиться максимально просто.
Источники событий должны поддерживать несколько подписчиков на события. Кроме того, должен поддерживаться сценарий, когда подписчики на события не подключены.
Как можно увидеть, цели в отношении событий очень похожи на цели в отношении делегатов. Вот почему языковая поддержка событий основана на механизмах поддержки делегатов.
Языковая поддержка событий
Синтаксис определения событий, а также подписки и отмены подписки на них является расширением синтаксиса для делегатов.
Для определения события используется ключевое слово event
.
public event EventHandler<FileListArgs> Progress;
Тип события (в этом примере EventHandler<FileListArgs>
) должен быть типом делегата. При объявлении события должен соблюдаться ряд соглашений. Как правило, тип делегата события имеет возвращаемый тип void.
Объявление события должно представлять собой глагол или глагольное словосочетание.
Если событие сообщает о том, что уже произошло, используйте прошедшее время. Для сообщения о том, что должно произойти, используйте глагол в настоящем времени (например, Closing
). Настоящее время часто указывает на то, что класс поддерживает какую-либо настройку. Одна из самых распространенных ситуаций — поддержка отмены. Например, событие Closing
может иметь аргумент, который указывает на то, должна ли продолжаться операция закрытия. В других ситуациях вызывающим объектам может предоставляться возможность изменения поведения путем изменения свойств аргументов события. Событие может вызываться для указания действия, которое алгоритму предлагается выполнить в следующую очередь. Обработчик событий может предписать выполнение другого действия, изменив свойства аргумента события.
Если нужно инициировать событие, то следует вызвать соответствующий обработчик событий с помощью синтаксиса вызова делегатов:
Progress?.Invoke(this, new FileListArgs(file));
Как описано в разделе, посвященном делегатам, оператор ?. позволяет легко предотвратить попытки вызова события, если на него нет подписчиков.
Подписка на событие производится с помощью оператора +=
:
EventHandler<FileListArgs> onProgress = (sender, eventArgs) =>
Console.WriteLine(eventArgs.FoundFile);
fileLister.Progress += onProgress;
Имя метода обработчика обычно содержит имя события с префиксом On, как показано выше.
Для отмены подписки на событие служит оператор -=
:
fileLister.Progress -= onProgress;
Важно, чтобы для выражения, представляющего обработчик событий, была объявлена локальная переменная. Благодаря этому при отмене подписки обработчик удаляется. Если вместо этого использовалось тело лямбда-выражения, то производится попытка удалить обработчик, который не был подключен, что не приводит ни к какому результату.
В следующей статье вы узнаете больше о типичных шаблонах событий и ознакомитесь с вариантами этого примера.