Полное руководство. С# 4.0
Шрифт:
Несмотря на то что при создании анонимной функции предпочтение следует теперь отдавать лямбда-выражениям, в качестве обработчика событий можно по- прежнему использовать анонимный метод. Ниже приведен вариант обработчика со бытий из предыдущего примера, измененный с целью продемонстрировать примене ние анонимного метода. // Использовать анонимный метод в качестве обработчика событий. evt.SomeEvent += delegate(int n) { Console.WriteLine("Событие получено. Значение равно " + n); };
Как видите, синтаксис использования анонимного метода в качестве обработчика событий остается таким же, как и для его применения вместе с любым другим типом делегата. Рекомендации по обработке событий в среде .NET Framework
В C# разрешается формировать какие угодно разновидности событий. Но ради со вместимости программных компонентов со средой .NET Framework следует придер живаться рекомендаций, установленных для этой цели корпорацией Microsoft.
Как правило, отправитель — это параметр, передаваемый вызывающим кодом с помощью ключевого слова this. А параметр е типа EventArgs содержит дополни тельную информацию о событии и может быть проигнорирован, если он не нужен. Сам класс EventArgs не содержит поля, которые могут быть использованы для передачи дополнительных данных обработчику. Напротив, EventArgs служит в ка честве базового класса, от которого получается производньгй класс, содержащий все необходимые поля. Тем не менее в классе EventArgs имеется одно поле Empty типа static, которое представляет собой объект типа EventArgs без данных. Ниже приведен пример программы, в которой формируется .NET-совместимое событие. // Пример формирования .NET-совместимого события. using System; // Объявить класс, производный от класса EventArgs. class MyEventArgs : EventArgs { public int EventNum; } // Объявить тип делегата для события. delegate void MyEventHandler(object source, MyEventArgs arg); // Объявить класс, содержащий событие. class MyEvent { static int count = 0; public event MyEventHandler SomeEvent; // Этот метод запускает событие SomeEvent. public void OnSomeEvent { MyEventArgs arg = new MyEventArgs; if(SomeEvent != null) { arg.EventNum = count++; SomeEvent(this, arg); } } } class X { public void Handler(object source, MyEventArgs arg) { Console.WriteLine("Событие " + arg.EventNum + " получено объектом класса X."); Console.WriteLine("Источник: " + source); Console.WriteLine; } } class Y { public void Handler(object source, MyEventArgs arg) { Console.WriteLine("Событие " + arg.EventNum + " получено объектом класса Y."); Console.WriteLine("Источник: " + source); Console.WriteLine; } } class EventDemo6 { static void Main { X ob1 = new X; Y ob2 = new Y; MyEvent evt = new MyEvent; // Добавить обработчик Handler в цепочку событий. evt.SomeEvent += ob1.Handler; evt.SomeEvent += ob2.Handler; // Запустить событие. evt.OnSomeEvent; evt.OnSomeEvent; } }
Ниже приведен результат выполнения этой программы. Событие 0 получено объектом класса X Источник: MyEvent Событие 0 получено объектом класса Y Источник: MyEvent Событие 1 получено объектом класса X Источник: MyEvent Событие 1 получено объектом класса Y Источник: MyEvent
В данном примере создается класс MyEventArgs, производный от класса EventArgs. В классе MyEventArgs добавляется лишь одно его собственное поле: EventNum. Затем объявляется делегат MyEventHandler, принимающий два параметра, требующиеся для среды .NET Framework. Как пояснялось выше, первый параметр содержит ссыл ку на объект, формирующий событие, а второй параметр — ссылку на объект класса EventArgs или производного от него класса. Обработчики событий Handler, опре деляемые в классах X и Y, принимают параметры тех же самых типов.
В классе MyEvent объявляется событие SomeEvent типа MyEventHandler. Это событие запускается в методе OnSomeEvent с помощью делегата SomeEvent, ко торому в качестве первого аргумента передается ссылка this, а вторым аргумен том служит экземпляр объекта типа MyEventArgs. Таким образом, делегату типа MyEventHandler передаются надлежащие аргументы в соответствии с требованиями совместимости со средой .NET. Применение делегатов EventHandler<TEventArgs> и EventHandler
В приведенном выше примере программы объявлялся собственный делегат со бытия. Но как правило, в этом не никакой необходимости, поскольку в среде .NET Framework предоставляется встроенный обобщенный делегат под названием EventHandler. (Более подробно обобщенные типы рассматриваются в главе 18.) В данном случае тип TEventArgs обозначает тип аргумента, передаваемого параметру EventArgs события. Например, в приведенной выше программе событие SomeEvent может быть объявлено в классе MyEvent следующим образом. public event EventHandler<MyEventArgs> SomeEvent;
В общем, рекомендуется пользоваться именно таким способом, а не определять собственный делегат.
Для обработки многих событий параметр типа EventArgs оказывается ненуж ным. Поэтому с целью упростить создание кода в подобных ситуациях в среду .NET Framework внедрен необобщенный
В данном примере параметр типа EventArgs не используется, поэтому в качестве этого параметра передается объект-заполнитель EventArgs.Empty. Результат выпол нения кода из данного примера следующий. Произошло событие Источник: MyEvent Практический пример обработки событий
События нередко применяются в таких ориентированных на обмен сообщениями средах, как Windows. В подобной среде программа просто ожидает до тех пор, пока не будет получено конкретное сообщение, а затем она предпринимает соответствую щее действие. Такая архитектура вполне пригодна для обработки событий средствами С#, поскольку дает возможность создавать обработчики событий для реагирования на различные сообщения и затем просто вызывать обработчик при получении кон кретного сообщения. Так, щелчок левой кнопкой мыши может быть связан с событием LButtonClick. При получении сообщения о щелчке левой кнопкой мыши вызывает ся метод OnLButtonClick, и об этом событии уведомляются все зарегистрирован ные обработчики.
Разработка программ для Windows, демонстрирующих такой подход, выходит за рамки этой главы, тем не менее, рассмотрим пример, дающий представление о принципе, по которому действует данный подход. В приведенной ниже програм ме создается обработчик событий, связанных с нажатием клавиш. Всякий раз, когда на клавиатуре нажимается клавиша, запускается событие KeyPress при вызове ме тода OnKeyPress. Следует заметить, что в этой программе формируются .NET- совместимые события и что их обработчики предоставляются в лямбда-выражениях. // Пример обработки событий, связанных с нажатием клавиш на клавиатуре. using System; // Создать класс, производный от класса EventArgs и // хранящий символ нажатой клавиши. class KeyEventArgs : EventArgs { public char ch; } // Объявить класс события, связанного с нажатием клавиш на клавиатуре. class KeyEvent { public event EventHandler <KeyEventArgs> KeyPress; // Этот метод вызывается при нажатии клавиши. public void OnKeyPress(char key) { KeyEventArgs k = new KeyEventArgs; if(KeyPress != null) { k.ch = key; KeyPress (this, k); } } } // Продемонстрировать обработку события типа KeyEvent. class KeyEventDemo { static void Main { KeyEvent kevt = new KeyEvent; ConsoleKeyInfo key; int count = 0; // Использовать лямбда-выражение для отображения факта нажатия клавиши. kevt.KeyPress += (sender, е) => Console.WriteLine(" Получено сообщение о нажатии клавиши: " + e.ch); // Использовать лямбда-выражение для подсчета нажатых клавиш. kevt.KeyPress += (sender, е) => count++; // count — это внешняя переменная Console.WriteLine("Введите несколько символов. " + "По завершении введите точку."); do { key = Console.ReadKey; kevt.OnKeyPress(key.KeyChar); } while(key.KeyChar != '.'); Console.WriteLine("Было нажато " + count + " клавиш."); } }
Вот, например, к какому результату приводит выполнение этой программы. Введите несколько символов. По завершении введите точку. t Получено сообщение о нажатии клавиши: t е Получено сообщение о нажатии клавиши: е s Получено сообщение о нажатии клавиши: s t Получено сообщение о нажатии клавиши: t . Получено сообщение о нажатии клавиши: . Было нажато 5 клавиш.
В самом начале этой программы объявляется класс KeyEventArgs, производный от класса EventArgs и служащий для передачи сообщения о нажатии клавиши об работчику событий. Затем объявляется обобщенный делегат EventHandler, опреде ляющий обработчик событий, связанных с нажатием клавиш. Эти события инкапсу лируются в классе KeyEvent, где определяется событие KeyPress.
В методе Main сначала создается объект kevt класса KeyEvent. Затем в це почку событий kevt.KeyPress добавляется обработчик, предоставляемый лямбда- выражением. В этом обработчике отображается факт каждого нажатия клавиши, как показано ниже. kevt.KeyPress += (sender, е) => Console.WriteLine(" Получено сообщение о нажатии клавиши: " + e.ch);
Далее в цепочку событий kevt.KeyPress добавляется еще один обработчик, пре доставляемый лямбда-выражением. В этом обработчике подсчитывается количество нажатых клавиш, как показано ниже. kevt.KeyPress += (sender, е) => count++; // count — это внешняя переменная