Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

public event CarEngineHandler AboutToBlow;

...

}

}

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

null
. Ниже приведена новая версия метода
Accelerate
класса
Car
:

public void Accelerate(int delta)

{

//
Если автомобиль сломан, то инициировать событие Exploded.

if (_carIsDead)

{

Exploded?.Invoke("Sorry, this car is dead...");

}

else

{

CurrentSpeed += delta;

// Почти сломан?

if (10 == MaxSpeed - CurrentSpeed)

{

AboutToBlow?.Invoke("Careful buddy! Gonna blow!");

}

// Все еще в порядке!

if (CurrentSpeed >= MaxSpeed)

{

_carIsDead = true;

}

else

{

Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);

}

}

}

Итак, класс

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

"За кулисами" событий

Когда компилятор C# обрабатывает ключевое слово event, он генерирует два скрытых метода, один с префиксом

add_
, а другой с префиксом
remove_
. За префиксом следует имя события С#. Например, событие
Exploded
дает в результате два скрытых метода с именами
add_Exploded
и
remove_Exploded
. Если заглянуть в код CIL метода
add_AboutToBlow
, то можно обнаружить вызов метода
Delegate.Combine
. Взгляните на частичный код CIL:

.method public hidebysig specialname instance void add_AboutToBlow(

class [System.Runtime]System.EventHandler`1<class CarEvents.

CarEventArgs> 'value') cil

managed

{

...

IL_000b: call class [System.Runtime]System.Delegate

[System.Runtime]System.

Delegate::Combine(class [System.Runtime]System.Delegate,

class [System.Runtime]System.
Delegate)

...

} // end of method Car::add_AboutToBlow

Как и можно было ожидать, метод

remove_AboutToBlow
будет вызывать
Delegate.Remove
:

public hidebysig specialname instance void remove_AboutToBlow (

class [System.Runtime]System.EventHandler`1

<class CarEvents.CarEventArgs> 'value') cil

managed

{

...

IL_000b: call class [System.Runtime]System.Delegate

[System.Runtime]System.

Delegate::Remove(class [System.Runtime]System.Delegate,

class [System.Runtime]System.
Delegate)

...

}

Наконец,

в коде CIL, представляющем само событие, используются директивы
.addon
и
.removeon
для отображения на имена корректных методов
add_XXX
и
remove_XXX
, подлежащих вызову:

.event class [System.Runtime]System.EventHandler`1

<class CarEvents.CarEventArgs> AboutToBlow

{

.addon instance void CarEvents.Car::add_AboutToBlow(

class [System.Runtime]System.EventHandler`1

<class CarEvents.CarEventArgs>)

.removeon instance void CarEvents.Car::remove_AboutToBlow(

class [System.Runtime]System.EventHandler`1

<class CarEvents.CarEventArgs>)

} // end of event Car::AboutToBlow

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

Прослушивание входящих событий

События C# также упрощают действие по регистрации обработчиков событий на стороне вызывающего кода. Вместо того чтобы указывать специальные вспомогательные методы, вызывающий код просто применяет операции

+=
и
– =
напрямую (что приводит к внутренним вызовам методов
add_XXX
или
remove_XXX
). При регистрации события руководствуйтесь показанным ниже шаблоном:

// ИмяОбъекта.ИмяСобытия +=

// new СвязанныйДелегат(функцияДляВызова);

Car.CarEngineHandler d =

new Car.CarEngineHandler(CarExplodedEventHandler);

myCar.Exploded += d;

Отключить от источника событий можно с помощью операции

– =
в соответствии со следующим шаблоном:

// ИмяОбъекта.ИмяСобытия - =

// СвязанныйДелегат(функцияДляВызова);

myCar.Exploded -= d;

Кроме того, с событиями можно использовать синтаксис группового преобразования методов:

Car.CarEngineHandler d = CarExplodedEventHandler;

myCar.Exploded += d;

При наличии таких весьма предсказуемых шаблонов переделайте вызывающий код, применив на этот раз синтаксис регистрации событий С#:

Console.WriteLine("***** Fun with Events *****\n");

Поделиться с друзьями: