Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 75
Текст из файла (страница 75)
В качестве альтернативы тип может просто реализовать 1са11есстоп<т> из пространства имен Бузгет. са11еас1апя. селекте, потому что он также реализует 1епптегаь1е<т>. применение синтаксиса инициализатора коллекции демонстрируется в следующем коде: азтпд Буяаекп аятпд Буясет.Са11есатапя.йепег1а) риЬ11с а1аяя Евр1ауее ( рпЬ11с ясгтпд Мате ( Бег) яег) ) ) рпЬ11с с1азя Со111п1С1а11яегкхзтр1е ( ясаста чата Матп() ( чаг аече1ортепстеат пеи 11яг<Етр1оуее> ( пеи Етр1оуее ( Мате = "Мтспае1 Ва1гоп" ), печ Етр1оуее ( Маве = "Баттг Маапеепзпз1аг" ), пеы Етр1оуее ( Мате = "Реаег 61ЬЬапя" ) Сопяо1е.ыгтае11пе( Брече1ортепа Тезт:" )к гогеаап( чаг етр1оуее Тп с)ече1ортепатеат ) ( Сопяа1е.игтгеввпе( "1Г" ь етр1оуее.нате ) ) Здесь "за кулисами" компилятор генерирует изрядный объем кода.
Для каждого элемента в списке инициализации генерируется вызов метода АсЫ коллекции. Обратите внимание, что для инициализации каждого экземпляра в списке инициализации также использовался новый синтаксис. Как упоминалось ранее, тип коллекции должен реализовывать 1са11еастоп<т> или 1епптегаЬ1е<т> и общедоступный метод Ас(с(. Если он этого не делает, возникнет ошибка времени компиляции. Вдобавок коллекция должна реализовывать только одну специализацию 1са11есстап<т>; те. она может реализовывать |со11еас1ап<т> толь- но для одного типа Т.
И, наконец, каждый элемент списка инициализации коллекции должен быть неявно преобразуемым к типу Т. 282 Глаза 9 Резюме В атой главе был представлен краткий обзор работы массивов в С1Л и СЭ, в плане подготовки к дискуссии об обобщенных типах коллекций. После обзора обобщенных типов коллекций.
определенных в яузсев. Сс11ессаопз. 0епегас, были затронуты вопросы эффективности и применимости и представлены полезные типы, определенные в пространстве имен Буз сеа. с о11ес стопа . 0Ьб ессмобе1. Затем рассматривались перечислители. Было показано, как создавать эффективные перечислители с помощью у1е16-блоков итераторов, появившихся в языке СЭ 2.0. И, наконец, был продемонстрирован новый синтаксис, появившийся в Св 3.0, который упрощает инициализацию коллекций во время создания экземпляров. Хотя в атой главе каждый тип коллекции подробно не рассматривался, все же после ее прочтения вы должны быть вооружены информацией, необходимой для сознательного выбора, когда и какой тип обобщенных коллекций следует использовать.
Тончайшие подробности, касающиеся АР1-ннтерфейсов типов коллекций, можно найти в документации МЗВп. В следующей главе речь пойдет о делегатах, анонимных методах и событиях. Анонимные методы удобны для создания встроенного вызываемого кода в точке регистрации кода обратного вызова. гллвл 10 делегаты, анонимные функции и еобытия д елегаты обеспечивают встроенный, поддерживаемый языком механизм определения и выполнения обратных вызовов. Их гибкость позволяет определять точную сигнатуру обратного вызова, и эта информация становится частью самого типа делегата.
Анонимные функции — форма делегатов, позволяющая сократить некоторую часть синтаксиса делегатов, который во многих случаях бывает громоздким и утоми- 1 тельным . На делегатах построена поддержка механизма событий в С!! и платформе .!ЧЕТ. События представляют собой унифицированный шаблон привязки реализаций обратных вызовов — даже по нескольку экземпляров сразу — к коду, инициирующему обратный вызов. Обзор делегатов СЬК предоставляет исполняющую систему, которая явно поддерживает гибкий механизм обратных вызовов. С самого начала времен — по крайней мере, со времен появления ЪЧпс1оэгз — всегда существовала необходимость в функциях обратного вызова, которые система либо какая-то другая сущность вызовет в определенный момент времени, чтобы известить о чем-нибудь интересном. В конце концов, обратные вызовы являются удобным механизмом, посредством которого пользователи могут расширить функциональность компонента.
Даже наиболее базовые компоненты приложения 1Ч1п32 С!!1 — оконные процедуры — представляют собой функции обратного вызова, регистрируемые в системе. Система вызывает такую функцию всякий рзз. когда нужно известить о поступлении некоторого события в окно. Этот механизм работает вполне хорошо в программной среде на базе языка С. Все стало несколько сложнее с распространением объектно-ориентированных языков вроде С++. Разработчики немедленно захотели, чтобы система могла вызывать методы экземпляров на объектах, а не только глобальные функции или статические методы. Существует много решений этой проблемы.
Но независимо от того, какое из них используется, в итоге все сводится к тому. что где-то кто-то должен хранить указатель на экземпляр объекта и вызывать метод экземпляра через этот указатель на него. Реализации обычно состоят из переходника 11!тип!г), который представляет собой не что иное адаптер, такой как промежуточный блок данных или кода, вызывающий метод 1 Даже лучшим, чем анонимные функции, средством являются лямбда-выражения, которым посвящена глава 15. 284 Глава 10 экземпляра через указатель на него'. Переходник — в действительности функция, зарегистрированная в системе. За многие годы в С++ было разработано немало изобретательных реализаций переходников. Ваш доверчивый автор с сентиментальной нежностью может вспомнить множество примеров такого дизайна.
В настоящее время делегаты являются предпочтительным способом реализации обратных вызовов в СЬВ. Делегат имеет смысл представлять как хорошо известный указатель на функцию, в качестве которой может выступать статический метод или метод экземпляра. Экземпляр делегата — это в точности то же самое, что переходник, но в то же время он является полноправным гражданином в стране СЬК. Фактически, при объявлении делегата в своем коде компилятор СЭ генерирует класс-наследник мп1с1сазгпе1еоасе, и СЬВ реализует все интересные методы делегата динамически, во время выполнения. Вот почему вы не увидите никакого 1Ь-кода, стоящего за методами делегата, если заглянете в скомпилированный модуль с помощью !ЬРАЗМ.
Делегат содержит два полезных поля. Первое хранит ссылку на объект, а второе— указатель на метод. При вызове делегата вызывается метод экземпляра на содержащейся в нем объектной ссылке. Однако если ссылка на объект равна пп11, то исполняющая система понимает это так, что метод является статическим. Более того, вызов делегата — синтаксически то же самое, что вызов обычной функции. Поэтому делегаты — блестящее средство реализации обратных вызовов. Как видите, делегаты представляет собой отличный механизм отделения вызываемого метода от вызывающего кода. Фактически вызывающий делегат код не имеет понятия, да и не нуждается в знании того, вызывается метод экземпляра или статический метод, либо на каком именно экземпляре производится вызов. Для вызывающего кода это просто вызов произвольного кода.
Он может получить экземпляр делегата любым подходящим способом, при этом полностью абстрагируясь от сущности, которую он на самом деле вызывает. Задумайтесь на минутку об элементах пользовательского интерфейса в диалоговом окне, таких как кнопка отправки, и о том, насколько много внешнюс сторон могут быть заинтересованы в знании факта выбора этой кнопки. Если класс, представляющий кнопку, должен напрямую вызывать зти заинтересованные стороны, ему нужно обладать подробными знаниями о компоновке этих заинтересованных сторон, или объектов, и знать, какие именно их методы должны быть вызваны.
Ясно, что такое требование приводит к чрезмерной зависимости между кнопкой и заинтересованными сторонами, причем зависимость эта чрезвычайно сложна и превращает сопровождение кода в кошмар. Здесь на помощь приходят делегаты и разрывают эту связь. Теперь заинтересованные стороны должны только зарегистрировать делегат с кнопкой, и этот делегат предварительно сконфигурирован так, что может вызывать любые методы, которые им нужны. Этот механизм отделения описывает события, как поддерживаемые СЬК.
Дополнительные сведения о событиях СЬВ можно найти в разделе "События" далее в главе. А пока давайте посмотрим, как создаются и используются делегаты в Сз. Создание и использование делегатов Объявления делегатов выглядят почти так же, как объявления абстрактных методов, за исключением того, что снабжаются одним дополнительным ключевым словом— бе1еозсе.
Вот как выглядит допустимое объявление делегата: роо11с бе1еоасе бопо1е Ргосеззаезп1гз~ бопо1е х, бооо1е у 1 Различные стили переходников описаны по адресу Лтср:УУеее.еМ-редаа.огЧУетв1/тлопю Делегаты, анонимные функции и события 285 Когда компилятор С» встречает зту строку, он определяет тип-наследник мп1г1саяг ре1едасе, который также реализует метод по имени 1пуоке, имеющий в точности ту же сигнатуру, что и метод, описанный в объявлении делегата.
Для практических нужд этот класс выглядит следующим образом: рпЬ1тс с1аяя Ргосеяяяеяп1тз: Яуятещ.нп1ттсаятОе1аоасе ( РиЬ11с бопЬ1е 1пуоке( бопЬ1е х, бопЬ1е у )) /! Остальное опущено для ясности Несмотря на то что компилятор создает тип, подобный приведенному, он также абстрагирует использование делегатов за синтаксическими сокращениями. Обычно для вызова делегата применяется синтаксис, подобный вызову функции, а не прямое обращение к методу 1пуо)ге.
Все это будет показано ниже. При создании экземпляр делегата потребуется связать с вызовом метода. Привязываемый метод может быть как статическим, так и методом экземпляра, обладающим сигнатурой, которая совместима с делегатом. Таким образом, типы параметров и тип возврата должны либо совпадать с объявлением делегата, либо быть неявно преобразуемыми в типы, указанные в объявлении делегата.
На заметку! В .НЕТ (.х сигнатура методов, привязанных к делегатам, должна была в точности совпадать с сигнатурой объявления делегата. В .НЕТ 2.0 зто требование было ослаблено и теперь допускается привязывать к делегату метод с совместимыми типами в объявлении. Одиночный делегат В следующем примере показан базовый синтаксис создания делегата. пятпд Яуясегн рпЬ1тс бе1едате бопЬ1е Ргосеяяяеяо1ся( бопЬ1е х, бопЫе у ) рпЫтс с1аяя Ртосеяяог ( РпЬ1тс Ртосеяяот( доиЫе 1ассот ) ГЬ1я.гассет = гассет; рпЬ1тс бопЬ1е Сощрпсе( бопЫе х, боиЬ1е у ) ( бооЫе геяп1Г = (х+у)*гассотг Сопяо1е.нт1сеььпе( "1пягапсеяеяи1гя: (О)", геяп1Г ) тесигп геяп1т; РпЫтс ясас1с бооЫе Ятас1сСощрпсе ( босЬ1е х, бопЬ1е у ) ( бопЬ1е тезп1т = (х+у) *0.52 Сопяо1е.нт1теь1пе( "Ятаттсяеяп1т: (О)", геяп1С )г тесптп теяп1Г1 ) рг1уате боиЫе гастог1 РпЬ1тс с1аяя Япстууо1пс ( ятас1с то1б Мдтп () ( Ртосезяот Ртос1 = пен Ртосезяот( 0.75 ); Ртасеяяот Ртас2 = пен Ртосеяяот( 0.83 ); 286 Глава (Р Ргосеввйеви1св Се1едасе1 е пе» Ргосеввйеви1гв (ргос1.Саврасе) Ргосеввйеви1св г)е1едаве2 пе» Ргосеввйеви1ев(ргос2.Соериве) Ргосеввйеви1ев г)е1едавеЗ = Ргосеввод.згаг1ссоярисе; с)опЬ1е сосЬапеп = Се1едаге1( 4, 5 се1едаге2 ( 6, 2 ) с(е1едагеЗ ( 5, 2 ); Сопэо1е.иггге11пе( "Вывод: (0)", соспгпеп ) В приведенном выше примере создаются три делегата.












