С учетом того, что очень многие специальные делегаты принимают экземпляр object в первом параметре и экземпляр производного от EventArgs класса во втором, предыдущий пример можно дополнительно упростить за счет применения обобщенного типа EventHandler, где Т — специальный тип, производный от EventArgs. Рассмотрим следующую модификацию типа Car (обратите внимание, что определять специальный тип делегата больше не нужно):
public class Car
{
...
public event EventHandler
public event EventHandler
}
Затем в вызывающем коде тип EventHandler можно использовать везде, где ранее указывался CarEngineHandler (или снова применять групповое преобразование методов):
Console.WriteLine("***** Prim and Proper Events *****\n");
// Создать объект Car обычным образом.
Car c1 = new Car("SlugBug", 100, 10);
// Зарегистрировать обработчики событий.
c1.AboutToBlow += CarIsAlmostDoomed;
c1.AboutToBlow += CarAboutToBlow;
EventHandler
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();
На заметку! После финальной фигурной скобки в анонимном методе должна быть помещена точка с запятой, иначе возникнет ошибка на этапе компиляции.