Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 78
Текст из файла (страница 78)
не затрагивая остальное. Обе стороны выполняют требования согласованного контракта, который в данном случае включает специально сформированный делегат и средства его регистрации в генерирующей события сущности~. Этот шаблон использования, также известный под названием "издатель/подписчик" (рцЬйзЬ/вцЬвспЬе], настолько распространен, даже за пределами мира разработки пользовательских интерфейсов, что проектировщики исполняющей системы .)чЕТ позаботились об определении формализованного встроенного механизма событий. При объявлении события внутри класса компилятор реализует скрытые методы, позволяющие регистрировать и отменять регистрацию делегатов, которые вызываются при наступлении определенных событий. По сути, событие — это сокращение.
позволяющее сэкономить время. которое понадобилось бы для написания методов регистрации и отмены регистрации, управляющих цепочкой делегатов. Давайте взглянем на простой пример события, основанный на вышесказанном. Контракты и интерфейсы подробно рассматриваются в главе 5. Делегаты, анонимные функции и события 293 озбпд Яуягев; // Аргументы, переданные от пользовательского интерфейса // при возникновении события включения воспроизведения. раЬ11с а1азз Р1ауЕчептАтая: Ечептлгдз рпЬ)ес Р1аукчептлтяз( зтт1па 111епаве ) ( ЬЬ1з.г11епаве = 111епаве) ) ргтчате яагупа 1т1епаве; рпЬ11с ятсена Р11епаве ( Нет ( гетпгп Ят1епаве) ) ) рпЬ1тс с1азз Р1ауегб1 ( // Определить аобытие для уведомления о воспроизведении. рпЬ11с ечепт ечептнапб1ет<Р1ауЕчептлгаз> Р1аукчепт; рпЬ11с чогб ОзегргеяяебР1ау() ОпР1ау()г ртотестеб ч1гтпа1 чотб Опр1ау() ( // Инициировать событие.
ечептнапб1ег<Р1ауечептлгаз> 1оса1напб1ет = Р1ауЕчепт) 11( 1оса1Напб1ет )= пп11 ) ( 1оса1Напб1ег( тптз, пен Р1ауЕчептАгдя("яове111е.нач") ); ) рпЬ1тс с1азя СотеР1ауег ( раЬ11с СогеР1ауег() ( пт = лен Р1ауетО1()г // Регистрация обработчика события. пт.Р1ауЕчепт += тп1з.Р1аузоветЬ1па) ргтчате чо1б Р1ауяоветйепд( аЬ)ест зоогсе, Р1ауечептАгая агав ) ( // Воспроизведение файла. ) рттчате Р1ауегО1 и).; ) раЬ11а с1азя ЕпттуРотпт ( ятаттс чо1б Иатп() ( СогеР1ауег р1аует = лен СотеР1ауег() ) ) Несмотря на то что синтаксис агота простого события может показаться усложненным, общая идея состоит в создании четко определенного контракта, через который все заинтересованные стороны уведомляются о том, что пользователь желает воспроизвести файл.
Этот контракт инкапсулирован внутри класса Р1аукчептлгаз, который унаследован от Яуятев. Ечептйтаз (как описано ниже). События накладывают определенные правила на использование делегатов. Делегат должен что-нибудь возвращать и должен 294 Глава 1б принимать два аргумента, как показано в методе Р1зузотеГП1пд из предыдущего примера. Первый аргумент — это ссылка на объект, представляющий сторону, которая генерирует сообщение, а второй аргумент — тип, унаследованный от Буз сею.
ечепгйгнз. В этом производном классе определяются все специфичные для события аргументы. Нв заметку! В .НЕТ 1.1 приходилось явно определять тип делегата, стоящего за событием. Начиная с .НЕТ 2.0, можно использовать новый обобщенный делегат ечепгнапб1ег<т>, защищающий от этой рутинной работы. Обратите внимание на способ определения события внутри класса Р1ауегп1 с применением ключевого слова ечепг. Эа этим ключевым словом сначала следует определенный делегат события, а за ним — имя события.
в данном случае Р1ауЕчепс. Также отметъте, что член-событие объявлено с использованием обобщенного делегата ечепгнапб1ег<т>. При регистрации обработчиков с применением операции н= в качестве сокращения, можно предоставлять только имя метода для вызова, а компилятор создаст экземпляр ЕчепГНапб1ег<Т>, используя группу методов для делегирования правил присваивания, которые упоминались в предыдущем разделе. После операции з= можно дополнительно указать выражение, создающее новый экземпляр ЕчепГНапб1ег<Т>, как зто делалось при создании экземпляров делегатов, но если компилятор предлагает показанное сокращение, то зачем применять громоздкий синтаксис, затрудняющий чтение кода? Идентификатор Р1ауечепг означает две совершенно разные вещи, в зависимости от того, с какой точки зрения его рассматривать.
С точки зрения генератора события — в данном случае, Р1ауегОТ вЂ” событие Р1ауЕчепс используется в точности как делегат. Такое его применение можно видеть внутри метода ОпР1зу. Обычно метод, названный ОпР1ау, вызывается в ответ на щелчок на кнопке пользовательского интерфейса. Он уведомляет всех зарегистрированных слушателей о вызове через событие !делегат] Р1зуЕчепс. На заметку! При генерации событий существует популярный подход — инициировать их внутри метода ргогесгеб ч1ггпз1 по имени Оп<событие>, где <событие> заменяется именем события, в данном случае — ОпР1ау.
Подобным образом производные классы могут легко модифицировать действия, предпринимаемые, когда должно быть инициировано событие. В С№ необходимо проверить событие на равенство пп11, прежде чем вызывать его, иначе будет сгенерировано исключение ни11негегепсеехсерг1оп. Перед проверкой на пс11 метод ОпР1зу создает локальную копию события. Это позволяет избежать условия состязаний, когда событие устанавливается в пи11 из другого потока после выполнения проверки на пп11 и перед генерацией события. Как видно в конструкторе ОогеР1ауег, со стороны потребителя события идентификатор Р1ауЕчепс используется совершенно иначе.
Такова базовая структура событий. Как упоминалось ранее, события .!ЧЕТ вЂ” зто сокращения для создания делегатов и контрактов, с которыми нужно регистрировать зти делегаты. В доказательство этого можно просмотреть код И., полученный в результате компиляции предыдущего примера. "За кулисами" компилятор генерирует два метода абг! ОпР1зу и геточе ОпР1зу, которые вызываются, когда используются перегруженные операции += и -=.
Эти методы управляют добавлением и удалением делегатов в цепочке делегатов событий. В действительности компилятор С№ не позволяет вызывать эти методы явно, так что должны использоваться перегруженные операции. Может возникнуть вопрос, а есть ли какой-нибудь способ контролировать тело этих функций-членов, как зто делается со свойствами? Ответ — да, и используемый для этого синтаксис подобен синтаксису свойств. Делегаты, анонимные функции и события 295 Ниже показан измененный код класса Р1ауегО1, в котором демонстрируется явный способ обработки добавления и удаления операций событий. рцЬ11с с1азз Р1ауегО1 ( УУ Определить событие для уведомления о воспроизведении.
ргкчаее Ечепгнапб1ег<Р1аукчепсдгдв> р1аукчепег риЬ11с ечипе Ечепенап61ег<Р1аукчепгдгдв> Р1аукчепе ( агЫ ( р1ауЕчепг (Ечепснапк)1ег<Р1аукчеикагдв>) Пе1едаее.СоиЬьпе( р1аукчеик, ча1ие )г ) геиоче ( р1аукчепе (Ечепенап61ег<Р1аукчепгдгдв>) Пе1едаие.нешоче( р1аукчепг, ча1ие ); ) ) рцЬ11с чогб Озегггеззег(Р1ау О ОпР1ау(); ) ргосессеб ч1гсца1 чо1С ОпР1ау() ( /! Инициировать событие. Ечепинапб1ег<Р1аукчепкдгда> 1оса1нап61ег = ргауЕчеиег 11( 1оса1калб1ег != лц11 ) ( 1оса1налб1ег( ГЬ1з, лен Р1ауЕчелГАгдз("зоее111е.нач") ) ) ) Внутри разделов або и гепоче объявления события ссылка на добавляемый или удаляемый делегат осуществляется по ключевому слову ча1ие, что идентично тому, как работает метод зес свойства. Данный пример использует Ое1едасе.
СоыЬуле и Ое1едасе . Еепоче для управления внутренней цепочкой делегатов по имени р1ауЕчепс. Пример выглядит несколько надуманным, поскольку механизм событий по умолчанию делает, по сути, то же самое, однако он предназначен только для целей демонстрации. На заметку! Явное определение пользовательских средств доступа к событиям может потребоваться для построения специализированного механизма хранения событий или для выполнения специальной обработки при регистрации и отмене регистрации событий.
А теперь заключительный комментарий относительно шаблонов проектирования. На основе сказанного можно сделать вывод, что события идеальны для реализации шаблона проектирования "издатель/подписчик", когда множество слушателей регистрируются для получения уведомления о событии (о его публикации). Точно так же события .НЕТ удобно использовать для реализации формы шаблона ОЬзегчег (Обозреватель), когда некоторые сущности рсгистрируготся для погтучения уведомлений об изменении другой сущности.
Это лишь два шаблона проектирования. реализацию которых облегчают события. 296 Глава 10 Анонимные методы Очень часто приходится создавать делегаты для обратного вызова, которые делают нечто очень простое. Представьте, что реализуется простой механизм обработки массива целых чисел. Предположим, что должна быть спроектирована гибкая система, в которой процессор обрабатывает массив целых чисел по алгоритму, переданному в точке его вызова. Такой шаблон использования называется шаблоном б(гаге([у (Стратегия). Он позволяет выбирать различные стратегии вычислений за счет предоставления механизма, с помощью которого во время выполнения указывается используемый алгоритм. Делегаты представляют собой блестящий инструмент для реализации такой системы. Рассмотрим соответствующий пример: ивгпс Яувгее; риЬ1тс с(е1еоаге гпг Ртосзггагеоу( тпг х ) риЬ1тс с1аяя Ртосеявот ( рттчаге Ргосзггагеду ягтагеду( риЬ11с Ргосзггагеоу Яггагечу ( вег ( вггагеоу = ча1ие; ) риЬ11с гпг[) Ртосевв( 1пг[) актау ) ( гпг[) геяи1г = пеи Епг( агтау.гепдгЬ ); лот( тпг 1 = 04 т < аттау.ьепогьв ++1 ) теяи1г[т) = вггагесу( аттау[т) тегитп геяи1г; ) риЬ11с с1авв епгтуРотпг рг1чаге вгагтс тпг Ми1гтр1УВУ2 ( 1пг х ) ( тегигп х*2) рггчаге вгагтс тпг Ми1гтр1УВУ4( гпг х ) ( гегитп х*4; ) рттчаге вгагтс чотб Рг1пгпггау( тпг[) аттау ) ( Рот( гпг 1 = 0; 1 < аггау.ьепдг(н Сопяо1е.нтгге( "(0)", аггау[1) 11( т )= аттау.ьепсгЬ-1 ) ( Сопяо1е.нггге( ", ' )4 Сопяо1е.кт1ге( "1п" ); ) вгаг1с чогб Маги() ( УУ Создать массив целых чисел.
тпг[) 1пгесегя = пеи 1пг[) 1, 2, 3, 4 )' Ргосеввот ртос = пеи Ргосеявог(); ртос.зггагеоу = пеи Ргосзгтагеду( ЕпгтуРо1пг.Ми1гтр1УВУ2 ) Делегаты, анонимные функции и события 297 РгапГАггау( ргос.ргосевв(апкедегв) ргос.зсгасеоу = пеи Ргосзсгасеоу( ЕпсгуРоупг.Мц1ггр1уву4 ); Рггпсхггау( ргос.ргосезз([пседегз) Концептуально идея выглядит простой. Однако чтобы заставить ее работать, придется сделать несколько сложных вещей. Сначала необходимо определить тнп делегата для представления метода стратегии. В предыдущем примере зто тип делегата Ргосзкгасецу. Затем понадобится написать различные методы стратегий.














