Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 83
Текст из файла (страница 83)
На основе идентификатора Сопса1пег можно объявлять сколько угодно много обобщенных типов, пока они отличаются по количеству параметров типа. В пределах контекста показанных выше определений нельзя объявить другой тип по имени Сопсз1пег<Х, у>, даже несмотря на то, что в списке параметров указаны другие идентификаторы. Правила перегрузки имен для обобщенных объявлений основаны на количестве параметров типа, а не на именах, назначенных нх заполнителям. Объявляя обобщенный тип, вы тем самым объявляете открытый тип.
Он называется так потому, что полностью специфицированный тип еще не известен. Объявляя другой тип на основе определения обобщенного, вы объявляете то, что называется конструируемым типом, как показано ниже: риЬ11с с1ввв МуС1ввв<Т> ргтчвсе Сслввтсег<)вг> 11е1с1; рг1чвЬЕ Сссевтсег<Т> Ете1О2) Оба поля в приведенном объявлении МуС1авв<Т> относятся к конструируемому типу, поскольку они объявляют новый тип на базе обобщенного типа Соовз1сег<т>.
Тип Сов с а1оес< тог > является закрытым, поскольку все заданные аргументы типа сами являются закрытыми, т.е. он необобщенный. Однако не каждый конструируемый тип является закрытым. 11е1в)1 является закрытым типом, в то время как бте1<)2 — открытым, поскольку его финальный тип должен быть определен во время выполнения на основе аргументов типа из МХС1авз<Т>.
В СФ все идентификаторы объявлены и допустимы в пределах определенной области )контекста). В границах метода, например, любые локальные переменные, объявленные внутри фигурных скобок тела метода, доступны только в пределах этого метода. Аналогичные правила действуют по отношению к идентификаторам параметров-типов внутри обобщений. В предыдущем примере идентификатор т действителен только в пределах области объявления класса. Рассмотрим следующий пример вложенного класса: рвЬ)тс с1ввв МуС1ввв<Т> риЬ1>с с1ввв МуневсебС1ввв<а> ) Обобщения 315 Идентификатор Н действителен внутри контекста вложенного класса, и его нельзя использовать в объемлющем контексте объявления муС1азя<т>. Однако т можно использовать во вложенном классе, поскольку вложенный класс определен внутри контекста, в котором идентификатор Т действителен.
Обычно считается нежелательным скрывать идентификаторы внешних аргументов во вложенных контекстах, как и идентификаторы имен переменных внутри вложенных областей выполнения. Например, попробуйте разобраться в показанном ниже запутанном коде: рпЬ11с с1аяя МуС1аяя<Т> риЬ11с с1аяя Мунезсег(С1азз<Т> ( ргтчаге сопгатпгег<т> тте161я ясастс чотз Матп() ( // Что зто значит для МунеяпеПС1аяя? Мус1аяя<1пг> с1ояег(туре1пяьапсе = пп11; ) ) Когда закрытый тип Мус1аяз<1пг> объявляется в Матп, что зто означает для вложенного типа? Ответ — ничего! Несмотря на то что объявление муне я тес(с1ая я <т> использует тот же аргумент типа, оно не расширяется до такого: // Этого НЕ происходит! рпЬ11с с1аяя мус1аяя<1пг> ( рсь11с с1аяя мунеягепс1аяя<тпс> ) рг1чаге сопга1пгег<1пг> тге1г(1; Факт указания параметра типа для МуС1аяя<Т> не означает также указание Мунеясег(С1аяя<т>.
В действительности. аккуратнее описать результирующий МуС1аяя<тпс> следующим образом: рпЬ11с с1аяя МуС1аяз<тпг> ( рпЬ11с с1аяя Мунеясеос1аяя<Т> ( ) рг1чапе Соппагпсег<1пп> Гте1О1; Муне я Сег)С 1 а я я<Т> остается открытым, даже невзирая на то. что использует тот же идентификатор в своем списке параметров, что и содержащий его тип. На самом деле в фигурных скобках Мунеягег)С1аяя<т> внешний аргумент из МуС1аяя<т> скрыт от доступа идентификатором из внутреннего контекста. Лучше объявить его так; рпЬ11с с1аяя МуС1аяз<Т> рпЬ11с с1аяя Мукеясеос1аяя<Н> ргтчате Т 1ппегг1е1г)1я рг1чаге Н Тппегтте1о2я ) ргтчаге Сопса1псег<Т> Гте1О1; 316 Глава 11 згаььс чозс Мэ1п () мус1азз<гпс> <1озег)туре1пзсапсе = пс11; ) ) Теперь в области определения Мунезгес)С1азз<й> доступны оба параметра — Т и Р,.
Здесь следует отметить один момент: несмотря на то, что объявлена переменная закрытого типа МуС1аз з <Тпг >, это не подразумевает, что объявлены какие-то закрытые типы от Мукезгес)С1азз<й>. Обобщенные структуры и классы подобно обычным структурам и классам могут содержать статические типы. Однако каждый закрытый тип, базируюшийся на обобщенном типе, содержит собственные экземпляры этих статических типов. Если рассматривать каждый закрытый тип как отдельный конкретный тип, это имеет прямой смысл. Например, если в МуС1азз<т> объявлено статическое поле по имени муЧа1це, то закрытый тип мус1азз<1пс> имеет собственное статическое поле мус1азз<упс>.муча1це, которое никак не связано со статическим полем МуС1азз<1опо>.
МуЧа1це. Следовательно. если требуется совместное использование статических данных в нескольких закрытых типах, которые основаны на одном и том же обобщенном типе, то это должно обеспечиваться другими средствами. Один из возможных приемов предполагает наличие отдельного, необобщенного типа, содержащего статические данные, на который ссылаются обобщенные тины. Это обычно реализуется с помощью шаблона 8)пя)етоп )Одиночка).
Такую конструкцию можно также построить, создав обобщенный тип-наследник необобщенного типа и поместив совместно используемый статический член в необобщенный тип. На заметку! Имейте в виду, что обобщенные типы со статическими инициализаторами требуют, чтобы код инициализации запускался всякий раз, когда СЬВ создает закрытый тип на основе обобщенного. Сложные инициализаторы типов или статические конструкторы могут увеличить рабочий набор приложения, если на основе такого обобщенного типа создается слишком много закрытых типов. Например, создание структуры данных значительных размеров в инициализаторе обобщенного типа может стать причиной существенного расхода памяти, если из такого обобщенного типа будет формироваться множество конкретных типов.
Обобщенные интерфейсы Нарялу с классами и структурами можно также создавать объявления обобщенных интерфейсов. Эта концепция является естественным развитием обобщений структур и классов. Разумеется, многие интерфейсы, объявленные в базовой библиотеке классов .НЕТ 1.1. являются отличными кандидатами на замену их обобщенными версиями. Блестящим примером послужит ТкповегаЬ1е<Т>. Обобщенные контейнеры создают гораздо более эффективный код, чем контейнеры необобщенные, если содержат элементы типов значений, поскольку онн позволяют избежать излишней упаковки.
Вполне естественно, что любой обобщенный перечислимый интерфейс должен иметь средства перечисления обобщенных элементов внутри себя. Таким образом, Ткпцае гаЬ1е<Т> существует, и любые перечислимые контейнеры, которые вы реализуете самостоятельно, должны реализовывать этот интерфейс. В качестве альтернативы их можно получить бесплатно, унаследовав пользовательский контейнер от Со11есг1оп<Т>.
На заметку! Собственные типы коллекций должны наследоваться от типа Со11ессзоп<т> из пространства имен 5 уз сею. со11есс1опз . Оьб ессмос)е1. Другие типы, такие как ь1з с<т>, не предназначены для наследования, а должны использоваться в качестве низкоуровневого механизма хранения. В Со11есгаоп<Т> реализованы защищенные виртуальные методы, которые можно переопределить для настройки его поведения, в то время как в ьззс<т> такая возможность отсутствует. Обобщения 317 Обобщенные методы Сй поддерживает обобщенные методы. Любое объявление метода внутри структуры, класса или интерфейса может быть сделано обобщенным. Сюда входят статические и виртуальные нли абстрактные методы.
К тому же в обобщенных или необобщенных типах можно объявлять обобщенные методы. Чтобы объявить обобщенный метод, просто добавьте список параметров-типов в конец имени метода перед списком его параметров. В списке параметров метода можно объявлять любые типы, включая тип возврата метода, как обобщенные параметры.
Как и в случае с вложенными классами, не стоит скрывать идентификаторы типов из внешнего контекста, повторно используя те же самые идентификаторы во вложенном контексте, которым в данном случае выступает контекст обобщенного метода. Рассмотрим пример, в котором может быть полезен обобщенный метод. В следующем коде создается контейнер, в который будет добавлено содержимое другого обобщенного контейнера: пяспд Яуясещ; оябпд Яуятещ.Со11есгтопя.йепетас; роЬ11с с1аяв мусопсабпег<т>: 1еппщегаь1е<т> ( рпЬ11с чогб Абб( Т ЬСещ ) [ Ьщр1.Абб( Тгещ ); // Сопчеггег<Т1приг, ТОпгрпг> — новый тип делегата, введенный // в .КЕТ Ргащеног)п 2.0, который может быть привязан к методу, // знахщему, как преобразовать тип Т1прпг в тип ТОогрпп.












