Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 81
Текст из файла (страница 81)
Для простоты был выбран обобщенный тнп делегата рипс<>; это позволило не объявлять собственные типы обобщенных делегатов. В результате получена работающая обобщенная привязка. 306 Глава )0 Тип Я1пг)2пс) из предыдущего примера представляет собой блестящее введение в обобщения, которые рассматриваются в главе 11. Шаблон 81га1еяу Делегаты представляют собой удобный механизм для реализации шаблона Этга1е)ту (Стратегия). В основе своей шаблон В1га1еяу позволяет динамически заменять вычислительные алгоритмы в зависимости от ситуации, сложившейся во время выполнения. Например, рассмотрим распространенный случай сортировки группы элементов. Предположим, что требуется максимально быстрое выполнение сортировки. Из-за особенностей системы обеспечение желаемой скорости связано с большим расходом памяти для временного хранения элементов.
Это отлично подходит для коллекций относительно управляемого размера, но если коллекция разрастается до громадных размеров, может случиться, что объем памяти, необходимый для проведения быстрой сортировки, превысит емкость системных ресурсов. Для таких случаев можно предусмотреть алгоритм сортировки, который работает медленнее, но зато потребляет намного меньше системных ресурсов. Шаблон Ыта1е))у позволяет заменять эти алгоритмы во время выполнения, в зависимости от конкретных условий.
Этот пример, несмотря на свою надуманность, отлично иллюстрирует предназначение данного шаблона. Обычно шаблон 81га1е))у реализуется с помощью интерфейсов. Сначала объявляется интерфейс, который реализуют все стратегии. После этого потребитель алгоритма может не беспокоиться о том, какая именно реализация стратегии будет использована. На рис. 10.1 показана диаграмма, описывающая типичное применение этого шаблона.
Рис. 10.1. Типичная реализация шаблона 81га1ецу на основе интерфейсов Делегаты являются легковесной альтернативой применению интерфейсов для реализации простой стратегии. Интерфейсы — вто просто механизм для реализации программного контракта. Предположим, что объявление делегата используется для реализации контракта, а любой метод, соответствующий сигнатуре делегата, представляет собой конкретную стратегию. Теперь вместо хранения ссылки на абстрактный интерфейс стратегии потребитель просто хранит экземпляр делегата. Описанный сценарий иллюстрируется в следующем примере: ив1пс Яувгелп пззпд Бувгее.Со11есгзопз; роо11с с)е1еовге Аггау Боггзсгвседу) 1Со11есгзоп гнеСо11ессзоп )> ри)з11с с1ввв Сопвипег ( роь11с сопвсаег ( Боггяггвгеоу сегап11яггвгесу ) ( слав.всгвседу = с)ЕГвц1гзггасесу; ) Делегаты, анонимные функции и события 307 рг1уаке Богкясгясеоу якгягедуг рнЬ11с Яоггесгягеду Бггасяду ) Бес ( геспгп яггасеоуг ) яес ( якгакесу = ча1не; ) рпЬ11с уо1б Оояоюеногн)) г'г' Использовать стратегию.
АГГЯУ ЯОГКЯЯ = ЯскаСЕЯУ) ЮУСО11ЕСГЬОП ); /Г Сделать что-то с результатом. ) ргауяке АггяуЬ1яг юуСо11ессьопт ) рнЬ1кс с1аяя БогСА1осг1СЬюя ( якякас Аггау Боггуаяг) 1СЬ11есг1оп СнеСЬ11ескаоп ) ) /! Выполнить быструю сортировку. ) ягэк1с Аггау ЯогСБ1он) 1СЬ11есгаоп ГЬеСЬ11ескаоп ) ( // Выполнить медленную сор~ировку. ) ) После создания экземпляр объекта Сопэнюег получает стратегию сортировки по умолчанию, которая представляет собой не что иное, как метод, реализующий сигнатуру делегата ЯогСЯСгагеду.
В зависимости от условий. сложившихся во время выполнения, экземпляру Соляною предоставляется соответствующий делегат, и метод Сопяцюег. СЬЯоюеыог)с автоматически вызовет требуемую стратегию. С помощью свойства яогкяггакеду используемую стратегию можно даже изменять во время выполнения. Показанная реализация шаблона Втга1е)гу оказывается даже еще более гибкой, чем применение интерфейсов, поскольку делегаты могут привязываться как к статическим методам, так и к методам экземпляра.
Следовательно, создавать конкретные реализации стратегии, поторые также содержат некоторые данные состояния, необходимые для выполнения операции, можно до тех пор, пока делегат указывает на метод экземпляра класса, содержащего эти данные о состоянии. Аналогично, делегат может быть анонимным методом, возвращаемым свойством этого класса. Резюме Делегаты представляют собой первоклассный определенный и реализованный системой механизм унифицированного представления обратных вызовов. В данной главе были описаны различные способы объявления и создания делегатов разных типов, включая одиночные делегаты, цепочки делегатов, делегаты открытого экземпляра, а также анонимные методы, которые являются делегатами.
Вдобавон было показано, как применять делегаты в качестве строительных блоков событий. Делегаты можно использовать для реализации широкого разнообразия шаблонов проектирования, поскольку они представляют собой замечательное средство для определения программного контракта. Четко определенный контракт лежит в основе практически любого шаблона проектирования. В следующей главе подробно рассматриваются обобщения, которые определенно являются одним из наиболее мощных средств СЬК и языка СЗ для создания безопасного в отношении типов кода. глдвА 11 Обобщения п оддержка обобщений (яепег(сз] — одно из самых замечательных средств СЗ и .НЕТ.
Обобщения позволяют создавать открытые [ореп-епбед) типы, которые преобразуются в закрытые во время выполнения. Каждый уникальный закрытый тип сам по себе уникален. Создавать экаемпляры можно только для закрытых типов. При объявлении обобщенного типа указывается список параметров типа, для которых будут переданы аргументы-типы, позволяющие создать конкретный закрытый тип. Ниже показан пример: риЬ11с с1азз МуСо11есстоо<Т> ( рсвуьс МуСо11ессьоо() ( ) ргьчаге Т[] зсогаце) ) В данном случае объявлен обобщенный тип муСо11есс1оп<т>, который трактует тип внутри коллекции как неугочненный тип. В приведенном примере список параметров типа состоит только из одного типа.
При этом применяется синтаксис, который позволяет перечислять в угловых скобках обобщенные типы, разделяя их запятыми. Идентификатор Т вЂ” это на самом деле только указатель места заполнения, куда подставляется любой тип. В некоторой точке потребитель МуСо11есгуоп<Т> объявляет то, что называется закрытым типом, подставляя конкретный тип, который должен представлять Т. Например, предположим, что какая-то другая сборка желает создать контейнерный тип мусо11есс1оп<т>, содержащий члены типа Тпс, Это можно сделать так, как показано в следующем коде: риЬ11с чо1о 5овенегьоо() ( Мусо11ессьоп<ьос> со11есг1опогншпЬегз = пез Мусо11есг1оо<1ос> О ) МуСо11есгуоп<1пс>является примером закрытого типа. МуСо11есгуоп<1ог> может использоваться подобно любому другому объявленному типу, и он также следует всем тем же правилам.
которым подчиняются другие необобщенные типы. Единственное отличие в том, что он порожден от обобщенного типа. В точке создания экземпляра [Ь-код реализации МуСо11есс].оо<Т> подвергается ЛТ-компиляции таким образом, что все включения типа Т в реализации МуСо11ессьоп<Т> заменяются типом 1пс. Обратите внимание, что все уникально сконструированные типы, созданные из одного и того же обобщенного типа, фактически являются совершенно разными типами, которые не разделяют никаких неявных возможностей преобразования. Например, МуСо11есг1оо<1оод> — совершенно отличный от МуСо11ессуоо<1пс> тип, и поступать так, как показано ниже, не допускается: 310 Глава 11 уу ято РАБотлть ББ Буднт)1) рпЬ11с гого яоеенесноо( МуСо11есстоп<тпс> тпскпвЬегя МуСо11есстоп<1опд> 1опдкпеЬегя = 1пснпзьегя> // ОШИБКА! ) Если вы знакомы с правилами ковариантности массивов, которые дают возможность выполнить следуюшую операцию, то наверняка обрадуетесь, узнав, что в СЗ 4.0 был добавлен синтаксис, позволяющий делать то же самое с обобщенными типами: рпвттс го1о Ргосеяяясгзпдя( ясг1пд() вуясг1пдя ) оЬ)есс() оЬ)я = еуясг1пдя; Гогеасн( оЬ)есс о зп оЬ)я ) ( Сопяо1е.нгтгеътпе( о ); Разница в том, что в случае ковариантности массивов источник и цель операции присваивания относятся к одному и тому же типу — яуясет.
Аггау. Правила ковариантности массивов просто позволяют присваивать один массив другому — до тех пор, пока объявленный тип элементов массива является неявно преобразуемым во время компиляции. Однако в случае двух сконструированных обобщенных типов мы имеем дело с совершенно разными типами. Начиная с версии СЗ 4.0, в языке поддерживается ковариантность и контраваривантность между обобщенными интерфейсами и делегатами с типами-аргументами на основе ссылок. Это позволяет ослабить ограничения на воэможность неявного преобразования некоторых обобщенных типов и помогает создавать код, который является осмысленным без множества дополнительных преобразований.
Тема вариантности подробно рассматривается в разделе "Ковариантность и контравариантность" далее в главе. Разница между обобщениями и шаблонами С++ Не случайно синтаксис обобщений похож на синтаксис шаблонов С++, тем более что синтаксис всех прочих элементов С() основан на соответствующем синтаксисе С++. Этот подход позволяет опираться на имеющиеся знания. Это типично для СЗ, потому что проектировщики языка постарались упростить синтаксис и исключить излишнюю многословность. Однако здесь сходство и заканчиваегся, потому что обобщения СЗ ведут себя совершенно иначе, чем шаблоны С++, и если вы пришли из мира С++, то должны быть уверенными, что понимаете разницу.
В противном случае может случиться так, что вы станете пытаться применить знания шаблонов С++ таким способом, который просто не будет работать с обобщениями. ()~звное отличие между этими двумя средствами в том, что расширение обобщений происходит динамически, в то время как расширение шаблонов С++ является статичным. Другими словами, шаблоны С++ всегда разворачиваются во время компиляции. И одной этой причины достаточно для того, чтобы шаблоны С++ невозможно было поместить в библиотеки. Начиная изучать шаблоны С++.















