Г. Шилдт - С#4.0 Полное руководство (1160795), страница 120
Текст из файла (страница 120)
Параметр ковариантного типа объявляется с помощью ключевого слова опя, которое предваряет имя этого параметра. Для того чтобы стали понятнее последствия применения ковариантности, обратимся к конкретному примеру. Ниже приведен очень простой интерфейс 1МуСоЧа гйеп1р, в котором применяется ковариантность. В этом обобщенном интерфейсе поддеряиваетса ковариантность. роЫ>с ьпоетсасе 1нусоуатаепгг<осв Т> ( Т ОеСОЬ)есо(); ) Обратите особое внимание на то, как объявляется параметр обобщенного типа т.
Его имени предшествует ключевое слово опС. В данном контексте ключевое слово оос обозначает, что обобщенный тип т является ковариантным. А раз он ковариантный, то метод СесСЬз асс () может возвращать ссылку на обобщенный тип Т или же ссылку на любой класс, производный от типа Т. Несмотря на свою ковариантность по отношению к обобщенному типу т, интерфейс 1МуСог)а гсепт Р реализуется аналогично любому другому обобщенному интерфейсу. Ниже приведен пример реализации этого интерфейса в классе МуС1ая з.
Реализовать интерфейс 1нусоуатоеп1Р. с1азв Мус1аяя<Т> : 1мусочатоеп1Р<Т> ( Т оЬ11 роЬ11с МуС1аяя(Т т) ( оЬ3 = тг ) роЫ1с Т СеСОЬ)еся() ( геоотп оЬЗ; ) ) Обратите внимание на то, что ключевое слово опс не указывается еще раз в выражении, объявляющем реализацию данного интерфейса в классе МуС1аяз. Это не только не нужно, но и вредно, поскольку всякая попытка еще раз указать ключевое слово оп с будет расцениваться компилятором как ошибка. А теперь рассмотрим следующую просгую реализацию иерархии классов.
О Создать простую иерархию классов. с1аяя А1рла ( яят1пд паве; роЫ1с А1рьа(ягтгпэ и) ( паве = пг роыьс ясттпч сеснаве() ( тесотп пагаег ) уу ) с1аяя Вета : А1рла ( роЫьс Веса (ясттпч и): Ьаяе (и) О ) Как видите, класс Веяа является производным от класса А1рба. 628 Часть!. Язык С() С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой. Создать ссылку из интерфейса 1нусоуагоеп1Р на объект типа МуС1азв<А1рла>.
О Это вполне допустимо как при наличии ковариантности, так и без нее. 1нусочагоеп1Р<А1рла> А1рЬавег = лен МуС1авв<А1рЬа>(пен А1рЬа("А1рЬа ()1"))г Сопво1е.нг1Сеъгпе("Имя объекта, на который ссылается переменная А1рлакеГ: А1РЬакет.пеСОЬЗесС().оегнаще()): // А теперь создать объект МуС1аав<Веоа> и присвоить его переменной А1рлакег. *** Эта строка кода вполне допустима благодаря ковариантности.
*** А1рлааег = пен МуС1авв<ВеСа>(пен ВеСа("Вега ()1"))г Сопао1е.игсге11пе("Имя объекта, на который теперь ссылается "переменная А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Мусоуагоеп1Р. с1авв МуС1авз<Т> : 1нусоуагбеп1Р<Т> ( Т оЬфг роЬ11с МуС1азв(Т ч) ( оЬТ = чг ) роЬ11с Т СеСОЬЗесС() ( гелпгп оЬЗ; ) ) /! Создать простую иерархию классов. с1азв А1РЬа ( вог1пЧ паве; роЬ1ьс А1рЬа(вггьпс и) ( вате = и; ) Глава 18. Обобщения 629 рпЪ||с ясгьпд Сесмяте () ( гесигп вате; ) О с1аяя ВеСа : А1рла ( риЫ|с Веса(ясг|пэ и) : саяе(п) ( ) О с1аяя Чаг|апсесеис атас|с чс1б Ма1п() // Создать ссылку из интерфейса 1мусоуагоеп1Е на объект типа МУС1аяя<А1рва>.
// Это вполне допустимо как при наличии ковариантности, так и без нее. |Муссуагаеп|Г<л|рба> А|рванет = пен Мус|аяя<Л|рла>(п А|рва("А|рЛа 11")); Сепяо1е.иг|Сеъьпе("Имя объекта, на котарьй ссылается переменная "А|рванет: " ь Л|р)ивет.ее( ОЬ)ес(.() .Ееьмыпе ()); // А теперь создать объект МуС1аяя<Веса> и присвоить его переменной А1рлавег.
// *** Эта строка кода вполне допустима благодаря ксзариантнести. *** А1рлавес = пен Мус1аяя<Веса> (пен Веса (" Веса $1") ) 1 Сопяо1е.нг|Се||пе("Имя объекта, на который теперь ссылается переменная " + "А|рланег: " ь А|рванет.веСОЬ)есс().аесм ())1 Результат выполнения этой программы выглядит следующим образом. Имя объекта, на который ссылается переменная А1рланег: А1рла ()1 Имя объекта, на который теперь ссыпается переменная А1рванет: Веса ()1 Следует особо подчеркнуть, что переменной А1рлане 1 можно присвоить ссылку на объект типа муС1а за <Веса> благодаря только тому, что обобщенный тип т указан как ковариантный в интерфейсе 1муСоуа гвеп1С.
Для того чтобы убедиться в этом, удалите ключевое слово оиС из объявления параметра обобщенного типа Т в интерфейсе 1МУСоуагвеп1Р и попытайтесь скомпилировать данную программу еще раз. Компиляция завершится неудачно, поскольку строгая проверка на соответствие типов не разрешит теперь подобное присваивание. Один обобщенный интерфейс может вполне наследовать от другого. Иными словами, обобщенный интерфейс с параметром ковариантного типа можно расширить, как показано ниже.
рчь11с спсеггасе тмусочягсеп1Г2<сес т> : 1мусочягсеп1у<т> ( О Обратите внимание на то, что ключевое слово опС указано только в объявлении расширенного интерфейса. Указывать его в объявлении базового интерфейса не только не нужно, но и не допустимо. И последнее замечание: обобщенный тип Т допускается не указывать как ковариантный в объявлении интерфейса 1муСоуагбеп1р2.
Но при этом исключается ковариантность, которую может обеспечить расширенный интерфейс 630 часть!. язык С() 1мусо)гагсестр. Разумеется, возможность сделать интерфейс 1мусо))агОеп1Г2 инвариантным может потребоваться в некоторых случаях его применения. На применение ковариантности накладываются некоторые ограничения. Ковариантность параметра типа может распространяться только на тип, возвращаемый методом.
Следовательно, ключевое слово оиг нельзя применять в параметре типа, служащем для объявления параметра метода. Ковариантность оказывается пригодной только для ссылочных типов. Ковариантный тип нельзя использовать в качестве ограничения в интерфейсном методе. Так, следующий интерфейс считается недопустимым. риъгьс ьпсегсасе 1нусоуагоеп1Р2<оис Т> ( чогб м<ч>() вьете ч:т; О Ошибка, ковариантный тип т нельзя использовать как ограничение ) Применение контравариантности в обобщенном интерфейсе Применительно к обобщенному интерфейсу контравариантность служит сред-, ством, разрешающим методу использовать аргумент, тип которого относится к базовому классу, указанному в соответствующем параметре типа.
В прошлом тип аргумента метода должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Контравариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр контравариантного типа объявляется с помощью ключевого слова Тп, которое предваряет имя этого параметра. Для того чтобы стали понятнее последствия применения ковариантности, вновь обратимся к конкретному примеру. Ниже приведен обобщенный интерфейс 1МуСопгга ча гпеп1Г контравариантного типа. В нем указывается контравариантный параметр обобщенного типа Т, который используется в объявлении метода Б)тон () .
Это обобценный интерфейс, поддерживанций контравариантность. ришьс гпгегсасе 1мусопсгачагаепгг<ьп т> ( чоьс( Зпон(Т оЬЗ)г ) Как видите, обобщенный тип Т указывается в данном интерфейсе как контравариантный с помощью ключевого слова Тп, предшествующего имени его параметра. Обратите также внимание на то, что Т является параметром типа для аргумента о)оз в методе Э)тои () . Далее интерфейс 1мусопсгауагсеп1Г реализуется в классе мус1азз, как показано ниже. О Реализовать интерфейс 1нусопсгауагоеп1Р. с1аяя МуС1аяя<Т> .: 1нусопсгаиагбеп1Р<Т> ( рищьс чо1б Эпин (Т х) ( Сопяо1е.игьсеъгпе (х) ) ) ) В данном случае метод Э)гои ( ) просто выводит на экран строковое представление переменной х, получаемое в результате неявного обращения к методу ТОБСгъпд () из метода Хгфсейгпе () .
После этого объявляется иерархия классов, как показано ниже. Создать простую иерархию классов. с1аяв А1рпа ( Глава 18. Обобщения 631 рпЫгс очеггтбе яСгьпд тозгггпо() ( геТпгп "Это объект класса й1рЬа."; ) О ) с1аяя ВеТа : А1рла ( рпЫ1с очегггбе ягг1пд тоягггпо() гегигп "Это объект класса ВеТа."; ) О ) Ради большей наглядности классы й1рЬа и ВеСа несколько отличаются от аналогичных классов из предыдущего примера применения ковариантности.
Обратите также внимание на то, что метод тоВСгспд () переопределяется таким образом, чтобы возвращать тип объекта. С учетом всего изложенного выше, следующая последовательность операций будет считаться вполне допустимой. Создать ссылку из интерфейса 1мусопггауагоеп1Г<й1рла> на объект типа МуС1аяя<й1рЬа>.