Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Вот к какому результату приводит выполнение этой программы.Событие получено. Значение равно 1Событие получено. Значение равно 2
Обратите особое внимание на то, как в этой программе лямбда-выражение используется в качестве обработчика событий.evt.SomeEvent += (n) => Console.WriteLine("Событие получено. Значение равно " + n);
Синтаксис для использования лямбда-выражения в качестве обработчика событийостается таким же, как для его применения вместе с любым другим типом делегата.
Несмотря на то что при создании анонимной функции предпочтение следуеттеперь отдавать лямбда-выражениям, в качестве обработчика событий можно по-прежнему использовать анонимный метод. Ниже приведен вариант обработчика событий из предыдущего примера, измененный с целью продемонстрировать применение анонимного метода.// Использовать анонимный метод в качестве обработчика событий.evt.SomeEvent += delegate(int n) { Console.WriteLine("Событие получено. Значение равно " + n);};
Как видите, синтаксис использования анонимного метода в качестве обработчикасобытий остается таким же, как и для его применения вместе с любым другим типомделегата.Рекомендации по обработке событий в среде .NET Framework
В C# разрешается формировать какие угодно разновидности событий. Но ради совместимости программных компонентов со средой .NET Framework следует придерживаться рекомендаций, установленных для этой цели корпорацией Microsoft. Этирекомендации, по существу, сводятся к следующему требованию: у обработчиков событий должны быть два параметра. Первый из них — ссылка на объект, формирующий событие, второй — параметр типа EventArgs, содержащий любую дополнительную информацию о событии, которая требуется обработчику. Таким образом, .NET-совместимые обработчики событий должны иметь следующую общую форму.void обработчик(object отправитель, EventArgs е) { // ...}
Как правило, отправитель — это параметр, передаваемый вызывающим кодом спомощью ключевого слова 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
В приведенном выше примере программы объявлялся собственный делегат события. Но как правило, в этом не никакой необходимости, поскольку в среде .NETFramework предоставляется встроенный обобщенный делегат под названиемEventHandler. (Более подробно обобщенные типы рассматриваютсяв главе 18.) В данном случае тип TEventArgs обозначает тип аргумента, передаваемогопараметру EventArgs события. Например, в приведенной выше программе событиеSomeEvent может быть объявлено в классе MyEvent следующим образом.public event EventHandler<MyEventArgs> SomeEvent;
В общем, рекомендуется пользоваться именно таким способом, а не определятьсобственный делегат.
Для обработки многих событий параметр типа EventArgs оказывается ненужным. Поэтому с целью упростить создание кода в подобных ситуациях в среду .NETFramework внедрен необобщенный делегат типа EventHandler. Он может быть использован для объявления обработчиков событий, которым не требуется дополнительная информация о событиях. Ниже приведен пример использования делегатаEventHandler.// Использовать встроенный делегат EventHandler.using System;// Объявить класс, содержащий событие,class MyEvent { public event EventHandler SomeEvent; // использовать делегат EventHandler // Этот метод вызывается для запуска события. public void OnSomeEvent() { if(SomeEvent != null) SomeEvent(this, EventArgs.Empty); }}class EventDemo7 { static void Handler(object source, EventArgs arg) { Console.WriteLine("Произошло событие"); Console.WriteLine("Источник: " + source); } static void Main() { MyEvent evt = new MyEvent(); // Добавить обработчик Handler() в цепочку событий. evt.SomeEvent += Handler; // Запустить событие. evt.OnSomeEvent(); }}
В данном примере параметр типа 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 + " клавиш."); }}