Язык программирования C#9 и платформа .NET5
hee, hee, hee...Очевидно, что вы не захотите предоставлять другим приложениям возможность изменять то, на что указывает делегат, или вызывать его члены без вашего разрешения. С учетом сказанного общепринятая практика предусматривает объявление переменных-членов, имеющих типы делегатов, как закрытых.
Ключевое слово event
В качестве сокращения, избавляющего от необходимости создавать специальные методы для добавления и удаления методов из списка вызовов делегата, в языке C# предлагается ключевое слово
. В результате обработки компилятором ключевого слова event вы автоматически получаете методы регистрации и отмены регистрации, а также все необходимые переменные-члены для типов делегатов. Такие переменные-члены с типами делегатов всегда объявляются как закрытые и потому они не доступны напрямую из объекта, инициирующего событие. В итоге ключевое словоeventможет использоваться для упрощения отправки специальным классом уведомлений внешним объектам.eventОпределение события представляет собой двухэтапный процесс. Во-первых, понадобится определить тип делегата (или задействовать существующий тип), который будет хранить список методов, подлежащих вызову при возникновении события. Во-вторых, необходимо объявить событие (с применением ключевого слова
) в терминах связанного типа делегата.eventЧтобы продемонстрировать использование ключевого слова event, создайте новый проект консольного приложения по имени
. В этой версии классаCarEventsбудут определены два события под названиямиCarиAboutToBlow, которые ассоциированы с единственным типом делегата по имениExploded. Ниже показаны начальные изменения, внесенные в классCarEngineHandler:Carusing System;namespace CarEvents{public class Car{...// Этот делегат работает в сочетании с событиями Car.public delegate void CarEngineHandler(string msgForCaller);<b> // Этот объект Car может отправлять следующие события:</b>public event CarEngineHandler Exploded;public event CarEngineHandler AboutToBlow;...}}Отправка события вызывающему коду сводится просто к указанию события по имени наряду со всеми обязательными параметрами, как определено ассоциированным делегатом. Чтобы удостовериться в том, что вызывающий код действительно зарегистрировал событие, перед вызовом набора методов делегата событие следует проверить на равенство
. Ниже приведена новая версия методаnullклассаAccelerate():Carpublic void Accelerate(int delta){// Если автомобиль сломан, то инициировать событие Exploded.if (_carIsDead){Exploded?.Invoke("Sorry, this car is dead...");}else{CurrentSpeed += delta;<b> // Почти сломан?</b>if (10 == MaxSpeed - CurrentSpeed){AboutToBlow?.Invoke("Careful buddy! Gonna blow!");}<b> // Все еще в порядке!</b>if (CurrentSpeed >= MaxSpeed){_carIsDead = true;}else{Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);}}}Итак, класс
был сконфигурирован для отправки двух специальных событий без необходимости в определении специальных функций регистрации или в объявлении переменных-членов, имеющих типы делегатов. Применение нового объекта вы увидите очень скоро, но сначала давайте чуть подробнее рассмотрим архитектуру событий.Car"За кулисами" событий
Когда компилятор C# обрабатывает ключевое слово event, он генерирует два скрытых метода, один с префиксом
, а другой с префиксомadd_. За префиксом следует имя события С#. Например, событиеremove_дает в результате два скрытых метода с именамиExplodedиadd_Exploded(). Если заглянуть в код CIL методаremove_Exploded(), то можно обнаружить вызов методаadd_AboutToBlow(). Взгляните на частичный код CIL:Delegate.Combine().method public hidebysig specialname instance void add_AboutToBlow(class [System.Runtime]System.EventHandler`1<class CarEvents.CarEventArgs> 'value') cilmanaged{...IL_000b: call class [System.Runtime]System.Delegate[System.Runtime]System.Delegate::Combine(class [System.Runtime]System.Delegate,class [System.Runtime]System.Delegate)...