События
События Важная роль делегатов заключается в том, что на них основана модель событий С#. Применение событий вовсе не ограничено приложениями с графическим интерфейсом они могут быть исключительно полезными и в обычных консольных программах.
Как мы помним, класс Саг (а точнее, его метод Саг.SpeedUp()) в его текущем состоянии приводит к исключению при попытке увеличить скорость уже вышедшего из строя автомобиля. Однако исключение это очень сильное средство (если оно не перехватывается должным образом, выполнение программы будет прервано), и если есть возможность обойтись без исключения, лучше попробовать поступить именно так. Правильнее в этой ситуации будет использовать вместо исключения пользовательское событие, возникающее при переходе автомобиля в нерабочее состояние. Класс Car
Мы изменим наш класс Саг, добавив в него два события. Первое событие (AboutToBlow) будет происходить тогда, когда текущая скорость всего на 10 миль в час меньше максимально допустимой. Второе событие (Exploded) будет возникать тогда, когда пользователь пытается ускорить автомобиль, который уже вышел из строя. Создание любого события в С# состоит из двух этапов. Первый этап - создание делегата, который будет использован для вызова нужного нам метода при срабатывании события, а второй этап определение собственно события при помощи ключевого слова event. Класс Car
// Этот класс Саг будет посылать пользователю // сообщения о своем состоянии public class Car { // Переменная для хранения информации о состоянии // машины private bool dead; // Делегат. Он нужен, чтобы вызвать функцию или // функции при возникновении события public delegate void EngineHandler(string msg); // Два события public static event EngineHandler Exploded; public static event EngineHandler AboutToBlow; … } Класс Car
// Вызываем нужное событие а зависимости от состояния объекта Саг public void SpeedUp(int delta) { // Если автомобиль уже вышел из строя, генерируем // событие Exploded if (dead) { if (Exploded != null) Exploded(Sorry, this car is dead..."): else { currSpeed += delta; // Приближаемся к опасной черте? Генерируем // событие AboutToBlow if(10 = = maxSpeed - currSpeed) if (AboutToBlow != null) AboutToBlow( "Careful, approaching terminal speed!"); // Все нормально! Работаем как обычно if(currSpeed >= maxSpeed) dead = true; else Console. WriteLine(\t'CurrSpeed = {0}, currSpeed); }} Класс Car
Любое событие (в нашем случае событий два Exploded и AboutToBlow) внутренне представляется следующими членами: статическим классом, определенным как private; методом add_XXXX{); методом remove_XXXX(). Как работают события
Предположим, что мы создали объект класса Саг и теперь наша задача организовать реакцию на события, которые этот объект будет посылать. Если подумать, то задача заключается в создании метода, представляющего приемник события, то есть метода, вызываемого делегатом. Формулируем задачу еще конкретнее нам необходимо вызвать нужный вариант метода add_XXXX ( ), чтобы добавить наш принимающий метод в таблицу указателей на функции в делегате, с которым связано событие. Однако в С# вызвать скрытые методы add_XXXX() и remove_XXXX() напрямую запрещено это можно делать только при помощи перегруженных операторов += и -= Прием событий
Поэтому «подключить» приемник для прослушивания событий можно только при помощи следующего синтаксиса: // Начинаем прослушивание Саг.Exploded += new Car.EngineHandler(OnBlowUp); Если мы хотим «отключиться» от прослушивания событий, то, конечно, для этого нужен перегруженный оператор -=: // Прекращаем прослушивание: Car.Exploded -= new Car.EngineHandler(OnBlowUp); Прием событий
// настраиваем реакцию на события public class CarApp { public static int Main(string[] args) { Car cl = new Car(SlugBug, 100, 10); // Устанавливаем приемники событий Car.Exploded += new Car.EngineHandler(OnBlowUp); Car.AboutToBlow += new Car.EngineHandler(OnAboutToBlow); // Разгоняем машину for (int i = 0; i < 10; i++) cl.SpeedUp(20); return 0; } // Приемник OnBlowUp public static void OnBlowUp(string s) { Console.WriteLine(Message from car: {0}, s); } // Приемник OnAboutToBlow public static void OnAboutToBlow (string s) { Console.WriteLine(Message from car: {0}, s); } } Прием событий
// Служебный класс для приемников событий public class CarEventSink { // Приемник OnBlowUp для события Exploded public void OnBlowUp(string s) { Console. WriteLine("Message from car: {0}", s); } // Приемник OnAboutToBlow для события AboutToBlow public void OnAboutToBlow(string s) { Console.WriteLine(Message from car: {0}, s); } Объекты как приемники событий
public class CarApp { public static int Main(string[] args) { Car c1 = new Car("SlugBug", 100, 10); // Создаем объект с приемниками CarEventSink sink = new CarEventSink(); // Устанавливаем приемники Car.Exploded += new Car.EngineHandler(sink.OnBlowUp); Car.AboutToBlow += new Car.EngineHandler(sink.OnAboutToBlow); for(int i = 0; i < 10; i++) c1.SpeedUp(20); return 0; } Объекты как приемники событий