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

ЖАНРЫ

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

{

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

if (carIsDead)

{

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

}

...

}

На вызывающей стороне понадобится лишь модифицировать обработчики событий для приема входных параметров и получения сообщения через поле, доступное только для чтения. Вот пример:

static void CarAboutToBlow(object sender, CarEventArgs e)

{

Console.WriteLine($"{sender} says: {e.msg}");

}

Если

получатель желает взаимодействовать с объектом, отправившим событие, тогда можно выполнить явное приведение
System.Object
. Такая ссылка позволит вызывать любой открытый метод объекта, который отправил уведомление:

static void CarAboutToBlow(object sender, CarEventArgs e)

{

// Просто для подстраховки перед приведением

// произвести проверку во время выполнения.

if (sender is Car c)

{

Console.WriteLine(

$"Critical Message from {c.PetName}: {e.msg}");

}

}

Обобщенный делегат EventHandler<T>

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

object
в первом параметре и экземпляр производного от
EventArgs
класса во втором, предыдущий пример можно дополнительно упростить за счет применения обобщенного типа
EventHandler<T>
, где
Т
— специальный тип, производный от
EventArgs
. Рассмотрим следующую модификацию типа
Car
(обратите внимание, что определять специальный тип делегата больше не нужно):

public class Car

{

...

public event EventHandler<CarEventArgs> Exploded;

public event EventHandler<CarEventArgs> AboutToBlow;

}

Затем в вызывающем коде тип

EventHandler<CarEventArgs>
можно использовать везде, где ранее указывался
CarEngineHandler
(или снова применять групповое преобразование методов):

Console.WriteLine("***** Prim and Proper Events *****\n");

// Создать объект Car обычным образом.

Car c1 = new Car("SlugBug", 100, 10);

// Зарегистрировать обработчики событий.

c1.AboutToBlow += CarIsAlmostDoomed;

c1.AboutToBlow += CarAboutToBlow;

EventHandler<CarEventArgs> d = CarExploded;

c1.Exploded += d;

...

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

Понятие анонимных методов C#

Как было показано ранее, когда вызывающий код желает прослушивать входящие события, он должен определить

специальный метод в классе (или структуре), который соответствует сигнатуре ассоциированного делегата. Ниже приведен пример:

SomeType t = new SomeType;

// Предположим, что SomeDeletage может указывать на методы,

// которые не принимают аргументов и возвращают void.

t.SomeEvent += new SomeDelegate(MyEventHandler);

// Обычно вызывается только объектом SomeDelegate.

static void MyEventHandler

{

// Делать что-нибудь при возникновении события.

}

Однако если подумать, то такие методы, как

MyEventHandler
, редко предназначены для вызова из любой другой части программы кроме делегата. С точки зрения продуктивности вручную определять отдельный метод для вызова объектом делегата несколько хлопотно (хотя и вполне допустимо).

Для решения указанной проблемы событие можно ассоциировать прямо с блоком операторов кода во время регистрации события. Формально такой код называется анонимным методом. Чтобы ознакомиться с синтаксисом, создайте новый проект консольного приложения по имени

AnonymousMethods
, после чего скопируйте в него файлы
Car.cs
и
CarEventArgs.cs
из проекта
CarEvents
(не забыв изменить пространство имен на
AnonymousMethods
). Модифицируйте код в файле
Program.cs
, как показано ниже, для обработки событий, посылаемых из класса
Car
, с использованием анонимных методов вместо специальных именованных обработчиков событий:

using System;

using AnonymousMethods;

Console.WriteLine("***** Anonymous Methods *****\n");

Car c1 = new Car("SlugBug", 100, 10);

// Зарегистрировать обработчики событий как анонимные методы.

c1.AboutToBlow += delegate

{

Console.WriteLine("Eek! Going too fast!");

};

c1.AboutToBlow += delegate(object sender, CarEventArgs e)

{

Console.WriteLine("Message from Car: {0}", e.msg);

};

c1.Exploded += delegate(object sender, CarEventArgs e)

{

Console.WriteLine("Fatal Message from Car: {0}", e.msg);

};

// В конце концов, этот код будет инициировать события.

for (int i = 0; i < 6; i++)

{

c1.Accelerate(20);

}

Console.ReadLine;

На заметку! После финальной фигурной скобки в анонимном методе должна быть помещена точка с запятой, иначе возникнет ошибка на этапе компиляции.

И снова легко заметить, что специальные статические обработчики событий вроде

CarAboutToBlow
или
CarExploded
в вызывающем коде больше не определяются. Взамен с помощью синтаксиса
+=
определяются встроенные неименованные (т.е. анонимные) методы, к которым вызывающий код будет обращаться во время обработки события. Базовый синтаксис анонимного метода представлен следующим псевдокодом:

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