Г. Шилдт - С#4.0 Полное руководство (1160795), страница 94
Текст из файла (страница 94)
бе1еоасе 1пс гпСОр(1пс епб)г о1азв ЯСаСешепСЬашобабешо ( згагьо но1г( Ма1п() ( Блочное лямбда-выражение возврашает факториал передаваемого ему значения. 1ПСОр гаСС = П с> 'пс Гог(1пс 1=13 1 <= и; 1ЕЕ) с = 1 * сг гесогп г; ): Сопво1е.игьгеъьпе("Факториал 3 равен " е Гасг(3)); Сопзо1е.ис1СеЬьпе("Факториал 5 равен " е Гаог(5)); При выполнении этого кода получается следующий результат.
Факториал 3 равен б Факториал 5 равен 120 В приведенном выше примере обратите внимание на то, что в теле блочного лямбда-выражения объявляется переменная с, организуется цикл 5ог и используется оператор геспгп. Все эти элементы вполне допустимы в блочном лямбда-выражении. И в этом отношении оно очень похоже на анонимный метод.
Следовательно, многие анонимные методы могут быть преобразованы в блочные лямбда-выражения при обновлении унаследованного кода. И еще одно замечание: когда в блочном лямбдавыражении встречается оператор ге со гп, он просто обусловливает возврат из лямбдавыражения, но не возврат из охватывающего метода. Глава 15. Делегаты, события и ляыбда-выражения 493 И в заключение рассмотрим еще один пример, демонстрирующий блочное лямбдавыражение в действии. Ниже приведен вариант первого примера из этой главы, измененного с целью использовать блочные лямбда-выражения вместо автономных методов для выполнения различных операций со строками.
Пераый пример применения делегатов, переделанный с О целью использовать блочные лямбда-выражения. цяьпс Яуясев; !/ Объявить тип делегата. бе1еоаге ясгьпо Ясгноб(ясг1пд я) с1аяя Ояезсагеюепсьащ)абая ясао1с чо1б Маьп() ( Создать делегаты, ссылающиеся иа лямбда- выражения, выполняющие различные операции с символьными строками. О Замеиить пробелы дефисами. Яогноб Вер1асезрасея = я => Сопяо1е.нг1сеЬ1пе("Замена пробелов дефисами.") гетцгп я.яер1асе(' ', ' †'): ): Удалить пробелы.
ясгмоб Кещочеярасея = я => ( ясг1по Геюр ьпс 1~ Сопяо1е.нг1гевьпе("Удалеиие пробелов.") бог (1=0; 1 < я . Ьепчстпн 1++) 11(я(1) )= ' ') Гещр е= я(1); гегцгп сещр; )' // Обратить строку. Ясгноб Вечегве = я => ( ясгьпЧ Гещр = гпг Сопяо1е.нгггеьгпе("Обращеиие строки."); бог(О=О, Ь=я.Ьепдсп-1г 1 >= О) 1--, 1++) гещр т= я (1]; геоцгп геюр; ): ягг1пд ягг; О Обратиться к лямбда-выражениям с помощью делегатов. Ясгноб астор = Нер1асезрасея; 494 Часть!.
Язык С№ ваг = всгор("Это простой тест."); Сопво1е.игьгеььпе("Результирующая строка: " + ваг); Сопво1е . Хг1сеь1пе ( )," валор = Кещочезряаев; ваг = валор("Это простой тест."); Сопво1е.нг1геььпе("Результирующая строка: " + вгг); Сопво1е.нгьсеььпе(); валор = Кечегяе; вгг = яггор("Это простой теат."): сопво1е.хг1геь1пе("Результирующая строка: " + вгг); Результат выполнения кода этого примера оказывается таким же, как и в первом примере применения делегатов. Замена пробелов дееисями. Результирующая строка: Это-проатой-тест. Удаление пробелов.
Результирующая отрока: Этопростойтест. Обращение строки. Результирующая отрока: .тсет йотсорп отЭ События Еще одним важным средством СФ, основывающимся на делегатах, является событие. Событие, по существу, представляет собой автоматическое уведомление о том, что произошло некоторое действие. События действуют по следующему принципу: объект, проявляющий интерес к событию, регистрирует обработчик этого события.
Когда же событие происходит, вызываются все зарегистрированные обработчики этого события. Обработчики событий обычно представлены делегатами. События являются членами класса и объявляются с помощью ключевого слова ечепс. Чаще всего для этой цели используется следующая форма: ечепт делегат события нмя события; где делегат события обозначает имя делегата, используемого для поддержки события, а имя события — конкретный объект объявляемого события. Рассмотрим для начала очень простой пример. Очень простой пример, демонстрирующий событие. ов1пЧ Яувгещ; // Объявить тип делегата для события.
бе1еовге чоьб МуЕчепГНвпб1ег()) // Объявить клаас, содержащий событие. с1ввв Мукчепс ( рпЬ11с ечепа МуЕчепГНапд1ег яощеЕчепт; Глава 15. Делегаты, события и лямбда-выражения 495 // этот метод выэывается для запуска события. рпЬ11с ноьб ОпБовеЕчепг() ( ЕГ(Бовекнепг != пп11) БовеЕнепг(); с1авэ Ечепгпево // Обработчик события. воагьс ного Напб1ег() ( Сопво1е.нггге11пе("Произошло событие") ) вгаС1с чо1б Матп () ( МуЕчепг ечг = пем Мувчепг(); Добавить метод Напд1ег() в список событий. ечс.зовеечепс += напб1егг // Запустить событие. ечг.опзовеЕчепг(); ) ) Вот какой результат получается при выполнении этого кода.
Произошло событие Несмотря на всю свою простоту, данный пример кода содержит все основные элементы, необходимые для обработки событий. Он начинается с объявления типа делегата для обработчика событий, как показано ниже. бе1едаге нота МуЕчепСНапб1ег(); Все события активизируются с помощью делегатов. Поэтому тип делегата события определяет возвращаемый тип и сигнатуру для события. В данном случае параметры события отсутствуют, но их разрешается указывать. Далее создается класс события МуЕчепс.
В этом классе обълвляется событие ЯовеЕчепС в следующей строке кода. роЬ11с ечепг МуЕчепСНапб1ег БовеЕнепг; Обратите внимание на синтаксис этого объявления. Ключевое слово ечепс уведомляет компилятор о том, что объявляется событие. Кроме того, в классе МуЕчепС объявляется метод ОЬБовеЕчепС (), вызываемый для сигнализации о запуске события. Это означает, что он вызывается, когда происходит событие. В методе ОпЯовеЕчепс () вызывается обработчик событий с помощью делегата ЯовеЕчепС. ьт(зовекчепС != пп11) БовеЕнепг(); Как видите, обработчик вызывается лишь в том случае, если событие БовеЕчепс не является пустым. А поскольку интерес к событию должен быть зарегистрирован в других частях программы, чтобы получать уведомления о нем, то метод Оп Я овеЕчепС () может быть вызван до регистрации любого обработчика события, Но во избежание 496 Часть ).
язык С() вызова по пустой ссылке делегат события должен быть проверен, чтобы убедиться в том, что он не является пустым. В классе Енепс)зевс создается обработчик событий Напб1ег () . В данном простом примере обработчик событий просто выводит сообщение, но другие обработчики могут выполнять более содержательные функции. Далее в методе Ма1п () создается объект класса события МуЕнепс, а Напс(1ег () регистрируется как обработчик этого события, добавляемый в список. МуЕнепг енл = пен МуЕнепг() Добавить метод Напс1ег() в список событий. енв.зовеЕнепг += Напо1ег; Обратите внимание на то, что обработчик добавляется в список с помощью оператора +=.
События поддерживают только операторы += и -=. В данном случае иетод Напб1ег () является статическим, но в качестве обработчиков событий могут также служить методы экземпляра. И наконец, событие запускается, как показано ниже. Запустить событие. енп.опяопеЕнепл(); Вызов метода ОпБовеЕнепг () приводит к вызову всех событий, зарегистрированных обработчиком. В данном случае зарегистрирован только один такой обработчик, но их может быть больше, как поясняется в следующем разделе. Пример групповой адресации события Как и делегаты, события поддерживают групповую адресацию. Это дает возможность нескольким объектам реагировать на уведомление о событии. Ниже приведен пример групповой адресации собьпия. Продемонстрировать групповую адресацию события. ця1пд Буясев1 // Объявить тип делегата для события.
бе1ечаге ноьб МуЕнепгнапо1ег()у // Объявить делегат, содерваший событие. с1аяя Мукнепг ( рцЬ11с енепг МуЕнепвнапб1ег яовеЕнепт.; Этот метод вызывается для запуска события. рцЪ11с ноьб ОпзовеЕнепб() ( 11(ЯопеЕнепс != пц11) яовеЕнепг(); с1аяя Х ( рнЬ11с ноьб ХЬапб1ег() Сопяо1е.Иг1гевьпе("Событие получено объектом класса Х") ) Глава 15. Делегаты, события и лямбда-выражения 497 с1аяя У ( роЬ1гс чолб УЬапб1ег () ( Сопяо1е,нггсегьпе("Событие получено объектом класса у") ) с1аяя Ечепгбево2 ( ясас1с чоыл Напг(1ег() ( Сопяо1е.нггсег Еле("Событие получено объектом класса Ечепсоево") ) ясас1с чогб Маго() ( муечепг ечг = пен муечепг(); Х хОЪ = пен Х(); Х уОЬ = пен У()," Г! Добавить обработчики я список событий. ечс.эовеЕчепс += Напб1ег; ечг.зовевчепп += хОЬ.ХЬапб1ег; ечс.зовекчепг ь= уОЬ.УЬапс(1егг Запустить событие.
ечс.опзовеЕчепс(); СОЬяо1е.нг1сеъ1ПЕ()Г // Удалить обработчик. ечг.зовеечепо =- хОЬ.ХЬап61егг ечс.опзовеЕчепп()Г ) При выполнении кода этого примера получается следующий результат. Событие получено объектом класса Ечепсбево Событие получено объектам класса Х Событие получено объектом класса Х Событие получено объектом класса Ечепсоево Событие получено объектом класса У В данном примере создаются два дополнительных класса, Х и У, в которых также определяются обработчики событий, совместимые с делегатом МуЕчепгиапг(1ег. Поэтому эти обработчики могут быть также включены в цепочку событий. Обратите внимание на то, что обработчики в классах х и у не являются статическими. Это означает, что сначала должны быть созданы объекты каждого из этих классов, а затем в цепочку событий должны быть введены обработчики, связанные с их экземплярами.
Об отличиях между обработчиками экземпляра и статическими обработчиками речь пойдет в следующем разделе. Методы экземпляра в сравнении со статическими методами в качестве обработчиков событий Методы экземпляра и статические методы могут быть использованы в качестве обработчиков собьпий, но между ними имеется одно существенное отличие. Когда 498 Часть!. Язык С№ статический метод используется в качестве обработчика, уведомление о событии распространяется на весь класс.
А когда в качестве обработчика используется метод экземпляра, то события адресуются конкретным экземплярам объектов. Следовательно, каждый объект определенного класса, которому требуется получить уведомление о событии, должен быть зарегистрирован отдельно. На практике большинство обработчиков событий представляет собой методы экземпляра, хотя это, конечно, зависит от конкретного приложения. Рассмотрим применение каждой из этих двух разновидностей методов в качестве обработчиков собьпий на конкретных примерах. В приведенной ниже программе создается класс х, в котором метод экземпляра определяется в качестве обработчика событий.