Г. Шилдт - С#4.0 Полное руководство (1160795), страница 96
Текст из файла (страница 96)
Когда же обработчик событий удаляется из цепочки событий, то вызывается аксессор гевоче и в массиве ечпс осуществляется поиск ссылки на этот обработчик, передаваемой в качестве параметра ча1ое. Если ссылка найдена, то соответствующему элементу массива присваивается пустое значение (пи11), а значит, обработчик удаляется из цепочки событий. При запуске события вызывается метод опБогвевчепс ( ) .
В этом методе происходит циклическое обращение к элементам массива ечпс для вызова по очереди каждого обработчика событий. Как демонстрирует рассматриваемый здесь пример программы, механизм хранения обработчиков событий нетрудно реализовать, если в этом есп, потребность. Но для большинства приложений более подходящим оказывается используемый по умолчанию механизм хранения обработчиков событий, который обеспечивает форма оператора ечепс без аксессоров. Тем не менее аксессорная форма оператора ечепс используется в особых случаях.
Так, если обработчики событий необходимо выполнять в программе в порядке их приоритетности, а не в том порядке, в каком они вводятся в цепочку событий, то для их хранения можно воспользоваться очередью по приоритету. ПРИМЕЧАНИЕ В многопоточных приложениях обычно приходится синхронизировать доступ к аксессорам событий. Подробнее о многопоточном программировании речь пойдет в главе 23. Разнообразные возможности событий События могут быть определены и в интерфейсах. При этом события должны предоставляться классами, реализующими интерфейсы.
События могут быть также определены как абстрактные (абзсгасс). В этом случае конкретное событие должно быть реализовано в производном классе. Но аксессорные формы событий не могут быть абстрактными. Кроме того, событие может быть определено как герметичное (веа1еб). И наконец, событие может быть виртуальным, т.е. его можно переопределить в производном классе. Применение анонимных методов и Аямбда- выражений вместе с событиями Анонимные методы и лямбда-выражения особенно удобны для работы с событиями, поскольку обработчик событий зачастую вызывается только в коде, реализующем механизм обработки событий. Зто означает, что создавать автономный метод, как правило, нет никаких причин, А с помощью лямбда-выражений или анонимных методов можно существенно упростить код обработки событий.
Глава 15. Аеаегаты, события и аямбда-выражеиия 505 Как упоминалось выше, лямбда-выражениям теперь отдается большее предпочтение по сравнению с анонимными методами, поэтому начнем именно с них. Ниже приведен пример программы, в которой лямбда-выражение используется в качестве обработчика событий. О Использовать лямбдв-выраженне в качестве обработчика событий. ов1п9 Бузсевг Объявить тип делегата для события. бе1ечаге ного Мукчепгяапб1ег(1пг и); Объявить класс, содержавнй событие.
с1авв МуЕчепс ( ро)>11с ечепг Мукчепгнапг(1ег Бовевчепш этот метод вызывается для запуска события. роб11с чогб Опзовевчепг(1пг и) ( 1Г(БовеЕчепг )= по11) БовеЕчепс(п)г с1аяз Ьавтктакчепгоево ( вгагьс чо1б ма1п() ( Мувчепс ечг = пен Мукчепг(); Использовать лямбда-выраженме в качестве обработчика событий. енс.зовеЕчепс += (и) => Сопяо1е.нггсеЬгпе("Событие получено. Значение равно " я и) Запустить событие.
ечг.опзовеЕчепг(1)Г ечГ.ОпБовеЕчепс(2)г Вот к какому результату приводит выполнение этой программы. Событие получено. Значение равно 1 Событие получено. Значение равно 2 Обратите особое внимание на то, как в этой программе лямбда-выражение используется в качестве обработчика собьпий. енг.зовейнепс += (и) => Сопво1е.ягггеьгпе("Событие получено. Значение равно " т и) Синтаксис для использования лямбда-выражения в качестве обработчика событий остается таким же, как для его применения вместе с любым другим типом делегата.
Несмотря на то что при создании анонимной функции предпочтение следует теперь отдавать лямбда-выражениям, в качестве обработчика событий можно по- прежнему использовать анонимный метод. Ниже приведен вариант обработчика событий из предыдущего примера, измененный с целью продемонстрировать применение анонимного метода. 506 Часть (.
язык С№ // Использовать анонимный метод з качестве обработчика событий. ечг.зовеечепг += йе1едаге(лпг и) ( Сопяо1е.игггеъьпе("Событие получено. Значение равно " + и); ): Как видите, синтаксис использования анонимного метода в качестве обработчика событий остается таким же, как и для его применения вместе с любым другим типом делегата. Рекомендации по обработке событий в среде.НЕТ баташевой В С() разрешается формировать какие угодно разновидности событий. Но ради совместимости программных компонентов со средой .(х)ЕТ Егашетчог(с следует придерживаться рекомендаций, установленных для этой цели корпорацией М(сгояог(. Эти рекомендации, по существу, сводятся к следующему требованию: у обработчиков событий должны быть два параметра.
Первый из них — ссылка на объект, формирующий событие, второй — параметр типа ечепсйгдя, содержащий любую дополнительную информацию о событии, которая требуется обработчику. Таким образом, .)х)ЕТ- совместимые обработчики событий должны иметь следующую общую форму. чола обработчик(оЬЗесс отправитель, Ечзпсйгдз е) ( // ) Как правило, отправитель — это параметр, передаваемый вызывающим кодом с помощью ключевого слова сь1я. А параметр е типа ечепсйгдя содержит дополнительную информацию о событии и может быть проигнорирован, если он не нужен.
Сам класс ечепсйгдз не содержит поля, которые могут быль использованы для передачи дополнительных данных обработчику. Напротив, Ечептйгдя служит в качестве базового класса, от которого получается производный класс, содержащий все необходимые поля.
Тем не менее в классе Ечепгйгдя ИмЕетСЯ ОДНО ПОЛЕ Еврту типа ясас1с, которое представляет собой объект типа Ечепсйгдз без данных. Ниже приведен пример программы, в которой формируется Ь(ЕТ-совместимое событие. // Пример формирования .НЕТ-совместимого события. ияьпд Буягев) Объявить класс, производный от класса Ечепсйгдя. с1аяя Мукчяпгйгдя : Ечепсйгдя ( риЬ11с ьпс Ечепснпв; ) // Объявить тип делегата для события.
г)е1едагя чоьб МуЕчепснапб1зг(оЬ>есс яоигсе, Мукчепглгдя агд) // Объявить класс, содержащий событие. с1аяя МуЕчзпс ( ясас1с 1пс соипс = Оу рпЬ11с ечепс МукчепГНапг)1ег БовяЕчзпс; Глава 15. Делегаты, события и ляыбда-выражения 507 // этот метод запускает событие Яовевнепз.
рцЬ11с чотд ОпзовеЕчепс() ( муечепглгдя агд " пеи муечепглгдя(); 11(яовеЕчепс != по11) ( агд.ечепгхцв = соцпс++) БовеЕнепт(ГЬЯя, агд); ) ) с1азз Х ( роЬ11с чотд Напд1ег(оЬзесс зоцгсе, МуЕчепзягдз агд) ( сопяо1е.хгтсеьлпе("событие " + агд.ечепсхцв + получено объектом класса Х."); Сопяо1е.игасеЫпе("Источник: " т зоогсе); Сопяо1е.игасе11пе()) ) ) с1аяя Х ( рпЫдс чотд Напд1ег(оЬ)есс яопгсе, МуЕчепяягдз агд) ( Сопяо1е.иг1геЬЕпе("Событие " Ь агд.ЕчепСнцв а получено объектом класса У."); Сопяо1е.игасеьапе("Источникг " + яоцгсе); Сопяо1е.Хгьяетьпе()) ) ) с1аяя Енепспевоб ( ясасас чоьд Маял() ( Х оЫ = пеи Х()) у ОЬЯ = пеи т() г МуЕчепс ечс = пеня МуЕчепс()) // )(сбавить обработчик Напд1ег() в цепочку событий. енг.зовевчепз += оЬ1.Напд1ег) ечг.яовеЕнепс += ОЬЯ.Напд1ег) // Запустить событие, ечс.опзовеЕчепс(); ечс.опзовеЕчепь()г ) ) Ниже приведен результат выполнения этой программы.
Событие О получено объектом класса Х Источник: Мувчепя Событие О получено объектом класса У Источник: Мувчепз Событие 1 получено объектом класса Х Источник: Мувчепо 508 Часть (. Язык б№ Событие 1 получено объектом класса У Источник: Мувчепп В данном примере создается класс МуЕчепГАгоз, производный от класса Ечепгйгоя.
В классе МуЕчепсйгоз добавляется лишь одно его собственное поле: Ечепснппь Затем обълвляется делегат МуЕчепГНапг(1е г, принимающий два параметра, требующиеся для среды .)х)ЕТ Ргашегчог)с. Как пояснялось выше, первый параметр содержит ссылку на объект, формирующий событие, а второй параметр — ссылку на объект класса Ечепсй где или производного от него класса. Обработчики событий На ос(1е г ( ), определяемые в классах Х и у, принимают параметры тех же самых типов. В классе МуЕчепс объявляется событие БопгеЕчепс типа МуЕчепгнапс(1ег.
Это событие запускается в методе ОпБовеЕчепг () с помощью делегата БоыеЕчепс, которому в качестве первого аргумента передается ссылка СЬЕЯ, а вторым аргументом служит экземпляр объекта типа МуЕчепгйгпя. Таким образом, делегату типа МуЕчепГНапг(1ег передаются надлежащие аргументы в соответствии с требованиями совместимости со средой )ЧЕТ. Применение делегатов Ечеп~Напй1ее<ТЕчепйдгдв) иЕчеп1Напс11ег В приведенном выше примере программы объявлялся собственный делегат события. Но как правило, в этом не никакой необходимости, поскольку в среде .(ь(ЕТ Ргашетчог)с предоставляется встроенный обобщенный делегат под названием ЕчепГНапс(1ег<тЕчепСАгоз>.
(Более подробно обобщенные типы рассматриваются в главе 18.) В данном случае тип ТЕчепсйгоз обозначает тип аргумента, передаваемого параметру Ечепсйгоя события. Например, в приведенной выше программе событие Б отеЕчепг может быть объявлено в классе МуЕчеп г следующим образом. риЬ11с ечепг ечепгнапг(1ег<муечепгАгдя> яоиеечепг; В общем, рекомендуется пользоваться именно таким способом, а не определять собственный делегат. Для обработки многих событий параметр типа Ечепсйгоя оказываетсл ненужным. Поэтому с целью упростить создание кода в подобных ситуацилх в среду .НЕТ Ргашечгог)с внедрен необобщенный делегат типа Ечепгнапс(1е г.
Он может быть использован для объявленил обработчиков событий, которым не требуется дополнительная информация о событиях. Ниже приведен пример использования делегата Ечепснапс(1ег. // Использовать встроенный делегат ЕчепГНапб1ег. пягпд Яуягеиг Объявить класс, содержаший событие. с1аяя МуЕчепс ( рпЬ11с ечепс ЕчепГНапг)1ег яоиекчепс; // использовать делегат ЕчепГНапб1лг // Этот метод вызывается для запуска события. рпЬ11с чо1б Опзоыевчепс() ( 11(яозеЕчепс != пп11) ЯоыеЕчепс(ГЬ1я, Ечеппьгэя.Бирсу)Г Глава 15.
Делегаты, события и авибда-выражения 509 с1авв Ечепгбевоз всастс чо16 Иапг)1ег(оЬ)есс воцгсе, ЕчепГАгдв агд) ( Сопво1е.мг1сетдпе("Произошло событие"); Сопзо1е.иг1сеьгпе("Источник: " + воцгсе); ) вгаг1с чогб Ма1п() ( МуЕчепс ечс = пен ИуЕчепс(); /1 Добавить обработчик Напб1ег() в цепочку событий. ечг.яовеечепг т= напс1егг Запустить событие. ечс.опяовекчепь(); ) ) В данном примере параметр типа ЕчепГАгдз не используется, поэтому в качестве этого параметра передается объект-заполнитель ечепгйгдз.
евргу. Результат выполнения кода из данного примера следующий. Произошло событие Источник: МуЕчепс Практический пример обработки событий События нередко применяются в таких ориентированных на обмен сообщениями средах, как Х(пс(о(чя. В подобной среде программа просто оживает до тех пор, пока не будет получено конкретное сообщение, а затем она предпринимает соответствующее действие.