1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 78
Текст из файла (страница 78)
Сначала определяется делегатобработчика события MyEventHandler. Код класса MyEvent, как показано в следующейинструкции, начинается с определения трехэлементного массива обработчиков событийevnt.MyEventHandler[] evnt = new MyEventHandler[3];Этот массив предназначен для хранения обработчиков событий, которые добавлены вцепочку событий.
Элементы массива evnt инициализируются null-значениями поумолчанию.Приведем event-инструкцию, в которой используются событийные средствадоступа.424Часть I. Язык C#public event MyEventHandler SomeEvent {// Добавляем обработчик события в список,add {int i;for(i=0; i < 3; i++)if(evnt[i] == null) {evnt[i] = value;break;}if(i == 3)Console.WriteLine("Список обработчиков событий полон.");}// Удаляем обработчик события из списка.remove {int i;}}for(i=0; i < 3; i++)if(evnt[i] == value) {evnt[i] = null;break;}if(i == 3)Console.WriteLine("Обработчик события не найден.");При добавлении в список обработчика событий вызывается add-средство, и ссылкана этот обработчик (содержащаяся в параметре value) помешается в первыйвстретившийся неиспользуемый элемент массива evnt. Если свободных элементов нет,выдается сообщение об ошибке.
Поскольку массив evnt рассчитан на хранение лишь трехэлементов, он может принять только три обработчика событий. При удалении заданногообработчика событий вызывается remove-средство, и в массиве evnt выполняется поискссылки на обработчик, переданной в параметре value. Если ссылка найдена, всоответствующий элемент массива помещается значение null, что равнозначно удалениюобработчика из списка.При генерировании события вызывается метод OnSomeEvent().
Он в циклепросматривает массив evnt, по очереди вызывая каждый обработчик событий.Как показано в предыдущих примерах, при необходимости относительно нетруднореализовать собственный механизм хранения обработчиков событий. Для большинстваприложений все же лучше использовать стандартный механизм хранения, в котором неиспользуются событийные средства доступа. Однако в определенных ситуациях формаevent-инструкции, ориентированной на событийные средства доступа, может оказатьсявесьма полезной. Например, если в программе обработчики событий должны выполняться впорядке уменьшения приоритетов, а не в порядке их добавления в событийную цепочку, тодля хранения таких обработчиков можно использовать очередь по приоритету.Смешанные средства обработки событийСобытия можно определять в интерфейсах. “Поставкой” событий должны заниматьсясоответствующие классы.
События можно определять как абстрактные. Обеспечитьреализацию такого события должен производный класс. Однако события, реализованныеГлава 15. Делегаты и события425с использованием средств доступа add и remove, абстрактными быть не могут. Любоесобытие можно определить с помощью ключевого слова sealed. Событие может бытьвиртуальным, т.е. его можно переопределить в производном классе.Рекомендации по обработке событий в среде.NET FrameworkC# позволяет программисту создавать события любого типа.
Однако в целяхкомпонентной совместимости со средой .NET Framework необходимо следоватьрекомендациям, подготовленным Microsoft специально для этих целей. Центральное местов этих рекомендациях занимает требование того, чтобы обработчики событий имели двапараметра. Первый должен быть ссылкой на объект, который будет генерировать событие.Второй должен иметь тип EventArgs и содержать остальную информацию, необходимуюобработчику.
Таким образом, .NET-совместимые обработчики событий должны иметьследующую общую форму записи:void handler(object source, EventArgs arg) {// ...}Обычно параметр source передается вызывающим кодом. Параметр типаEventArgs содержит дополнительную информацию, которую в случае ненадобностиможно проигнорировать.Класс EventArgs не содержит полей, которые используются при передачедополнительных данных обработчику; он используется в качестве базового класса, изкоторого можно выводить класс, содержащий необходимые поля. Но поскольку многиеобработчики обходятся без дополнительных данных, в класс EventArgs включеностатическое поле Empty, которое задает объект, не содержащий никаких данных.Ниже приведен пример, в котором создается .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) {426Часть I.
Язык C#}}}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 EventDemo {public static void Main() {X ob1 = new X();Y ob2 = new Y();MyEvent evt = new MyEvent();// Добавляем обработчик handler() в список событий.evt.SomeEvent += new MyEventHandler(ob1.handler);evt.SomeEvent += new MyEventHandler(ob2.handler);}}// Генерируем событие.
evt.OnSomeEvent();evt.OnSomeEvent();Вот как выглядят результаты выполнения этой программы:Событие 0 получено объектом X.Источником является класс MyEvent.Событие 0 получено объектом Y.Источником является класс MyEvent.Событие 1 получено объектом X.Источником является класс MyEvent.Событие 1 получено объектом Y.Источником является класс MyEvent.В этом примере класс MyEventArgs выводится из класса EventArgs. В классеMyEventArgs добавлено только одно “собственное” поле — eventnum. В соответствииГлава 15. Делегаты и события427с требованиями .NET Framework делегат для обработчика событий MyEventHandlerтеперь принимает два параметра.
Как разъяснялось выше, первый из них представляетсобой объектную ссылку на генератор событий, а второй — ссылку на класс EventArgsили производный от класса EventArgs. В данном случае здесь используется ссылка наобъект типа MyEventArgs.Использование встроенного делегата EventHandlerДля многих событий параметр типа EventArgs не используется. Для упрощенияпроцесса создания кода в таких ситуациях среда .NET Framework включает встроенный типделегата, именуемый EventHandler. Его можно использовать для объявленияобработчиков событий, которым не требуется дополнительная информация.
Рассмотримпример использования типа EventHandler.// Использование встроенного делегата EventHandler.using System;// Объявляем класс события.class MyEvent {public event EventHandler SomeEvent; // Объявление// использует делегат EventHandler.}// Этот метод вызывается для генерирования// SomeEvent-событие.public void OnSomeEvent() {if(SomeEvent != null)SomeEvent(this, EventArgs.Empty);}class EventDemo {static void handler(object source, EventArgs arg) {Console.WriteLine("Событие произошло.");}Console.WriteLine("Источником является класс " +source + ".");public static void Main() {MyEvent evt = new MyEvent();// Добавляем обработчик handler() в список событий.evt.SomeEvent += new EventHandler(handler);}}// Генерируем событие.evt.OnSomeEvent();В данном случае параметр типа EventArgs не используется и вместо негопередается объект-заполнитель EventArgs.Empty.
Результаты выполнения этойпрограммы весьма лаконичны:Событие произошло.Источником является класс MyEvent.428Часть I. Язык C#Учебный проект: использование событийСобытия часто используются в таких средах с ориентацией на передачу сообщений,как Windows. В подобной среде программа просто ожидает до тех пор, пока не получитсообщение, а затем выполняет соответствующие действия.
Такая архитектура прекрасноподходит для обработки событий в стиле языка C#, позволяя создавать обработчикисобытий для различных сообщений и просто вызывать обработчик при полученииопределенного сообщения. Например, с некоторым событием можно было бы связатьсообщение, получаемое в результате щелчка левой кнопкой мыши. Тогда после щелчкалевой кнопкой мыши все зарегистрированные обработчики будут уведомлены о приходеэтого сообщения.Несмотря на то что разработка Windows-программ, в которых демонстрируется такойподход, выходит за рамки этой главы, все же обрисуем в общих чертах работу этогомеханизма.
В следующей программе создается обработчик событий нажатия клавиш.Событие называется KeyPress, и при каждом нажатии клавиши оно генерируетсяпосредством вызова метода OnKeyPress().// Пример обработки события, связанного с нажатием// клавиши на клавиатуре.using System;// Выводим собственный класс EventArgs, который// будет хранить код клавиши.class KeyEventArgs : EventArgs {public char ch;}// Объявляем делегат для события.delegate void KeyHandler(object source, KeyEventArgs arg);// Объявляем класс события, связанного с нажатием// клавиши на клавиатуре.class KeyEvent {public event KeyHandler KeyPress;// Этот метод вызывается при нажатии// какой-нибудь клавиши.public void OnKeyPress(char key) {KeyEventArgs k = new KeyEventArgs();if(KeyPress != null) {k.ch = key;KeyPress(this, k);}}}// Класс, который принимает уведомления о нажатии клавиши.class ProcessKey {public void keyhandler(object source, KeyEventArgs arg) {Console.WriteLine("Получено сообщение о нажатии клавиши: " + arg.ch);}}Глава 15.
Делегаты и события429// Еще один класс, который принимает уведомления// о нажатии клавиши.class CountKeys {public int count = 0;public void keyhandler(object source, KeyEventArgs arg) {count++;}}// Демонстрируем использование класса KeyEvent.class KeyEventDemo {public static void Main() {KeyEvent kevt = new KeyEvent();ProcessKey pk = new ProcessKey();CountKeys ck = new CountKeys();char ch;kevt.KeyPress += new KeyHandler(pk.keyhandler);kevt.KeyPress += new KeyHandler(ck.keyhandler);Console.WriteLine("Введите несколько символов. " +"Для останова введите точку.");do {ch = (char) Console.Read();kevt.OnKeyPress(ch);} while(ch != '.');}}Console.WriteLine("Было нажато " +ck.count + " клавиш.");При выполнении этой программы можно получить такие результаты:Введите несколько символов.
Для останова введите точку.тест.Получено сообщение о нажатии клавиши: тПолучено сообщение о нажатии клавиши: еПолучено сообщение о нажатии клавиши: сПолучено сообщение о нажатии клавиши: тПолучено сообщение о нажатии клавиши: .Было нажато 5 клавиш.Эта программа начинается с выведения класса KeyEventArgs, которыйиспользуется для передачи сообщения о нажатии клавиши обработчику событий. Затемделегат KeyHandler определяет обработчик для событий, связанных с нажатием клавишина клавиатуре. Эти события инкапсулируются в классе KeyEvent.Программа для обработки нажатий клавиш создает два класса: ProcessKey иCountKeys. Класс ProcessKey включает обработчик с именем keyhandler(),который отображает сообщение о нажатии клавиши.