Г. Шилдт - С#4.0 Полное руководство (1160795), страница 119
Текст из файла (страница 119)
Разумеется, в производный класс можно свободно добавлять его собственные параметры типа, если в этом есть потребность. В качестве примера ниже приведен вариант предыдущей иерархии классов, где в класс Сеп2 добавлен собственный параметр типа. // Пример добавления собственных параметров типа в производный класс. ивьпц Зуягещ; // Обобщенный базовый класс. с1аяя Сеп<Т> ( Т ОЬ; !/ Объявить переменную типа Т // Передать конструктору ссылку типа Т. риЫ1с Сеп(Т о) оЬ = о; ) // Возвратить значение переменной оЬ.
рпЫ1с Т СеСОЬ() геснгп ОЪ1 ) ) // Класс, производный от класса Сеп. В зтом классе О определяется второй параметр типа Ч. с1аяя Сеп2<Т, Ч> : Сеп<Т> ( Ч оЬ21 рчЫТс Сеп2(Т о, Ч о2): Ьаяе(о) ( оЬ2 = о2; риЫТс Ч СеСОЬ12() ( геснтп оЬ2; ) 622 Часть (. язык С№ У/ Создать объект класса беп2. с1аяя оепНтегоегао2 ( ягат1с чоыз Маьп() ( О Создать объект класса оеп2 с параметрами // типа яьггпч и тпг.
оеп2<яягьпч, тпя> х = пен беп2<яггьпд, шс>("Значение равно: ", 99) Сопяо1е.иг1се (х.оеСОЫ) ) г Сапяо1е. Нг1геьъпе (х.6еСОЬ)2 () ); ) ) Обратите внимание на приведенное ниже объявление класса 6еп2 в данном вариаигеиерархии классов. с1аяя оеп2<т, Ч> : оеп<т> В этом объявлении Т вЂ” это тип, передаваемый базовому классу 6еп; а )) — тип, характерный только для производного класса 6еп2. Он служит для объявления объекта оЬ2 и в качестве типа, возвращаемого методом 6еЬСЬ12 () . В методе Ма№п () создается объект класса беп2 с параметром Т типа яг г1пд и параметром (( типа Тпг. Поэтому код из приведенного выше примера дает следующий результат.
Значение равно: 99 Обобщенный производный класс Необобщенный класс может быть вполне, законно базовым для обобщенного производного класса. В качестве примера рассмотрим следующую программу. Пример необобщенного класса в качестве базового длн О обобценного производного класса. ояъпд Буясеыг О Необобыенный базовый класс. с1аяв Нопоеп ( ьпс пчзг риЬ11с Нопоеп(тпт 1) ( пып = 1~ ) роЬ11с тпя оеяноз() ( гегогп пчыг ) ) Обобыенньй производный класс.
с1аяя Сеп<Т> : Ноп6еп ( Т оЬ; Глава 18. Обобщения 623 роЫгс Сеп(Т о, 1пг ъ): Ьаве (г) ( оЬ = ог ) О Возвратить значение переменной оЬ. роЫгс Т ПеСОЬ() ( гесогп оЬг ) ) О Создать объект класса Пеп. с1авз Н1егпешоз воагьс чо1б Магп() ( // Создать объект класса Пеп с параметром типа вог1пч. Пеп<зсг1пд> н = пен Пеп<зсггпо>("Привет", 47); Сон во1е . Хг1 ге (н. ПеСОЬ () Сопво1е.иг1оепгпе (н.оеоыош () ); ) Эта программа дает следующий результат. Привет 47 В данной программе обратите внимание на то, как класс Пеп наследует от класса )(ослеп в следующем объявлении. с1авв Пеп<Т> : Нопоеп ( Класс Ыопбеп не является обобщенным, и поэтому аргумент типа для него не указывается.
Это означает, что параметр Т, указываемый в объявлении обобщенного производного класса реп, не требуется для указания базового класса )ЧопПеп и даже не может в нем использоваться. Следовательно, класс Пеп наследует от класса Ноппеп обычным образом, т.е. без выполнения каких-то особых условий. Переопределение виртуальных методов в обобщенном классе В обобщенном классе виртуальный метод может быть переопределен таким же образом, как и любой другой метод. В качестве примера рассмотрим следующую программу, в которой переопределяется виртуальный метод ПеСОЬ () .
О Пример переопределения виртуального метода в обобшенном классе. овгпо ЯувоешГ О Обобшенный базовый класс. с1авв Пеп<Т> ( ргосессеб т оьг роЫ>с Яеп(Т о) ( оЬ = о; ) // Возвратить значение переменной оЬ. Этот метод является виртуальным. 624 Часть!. Язык С№ роЬ11с ч1ггоа1 Т бегОЬ() Сопяо1е.нггге("Метод бегОЬ() из клааса беп" + " возвращает результат: ")( гегогп оЬ; ) ) // Клаас, производный от класса беп.
В этом класае переопределяется метод бегОЬ(). с1аяя беп2<Т> ." беп<Т> ( роЬ11с беп2(Т о) : Ьаяе(о) ( ) О Переопределить метод бегОЬ () . роЬ11с очеггьбе Т бегОЬ() ( сопяо1е .Мгьге (" метод бегОЪ () из класса беп2" + " возвращает результат: "); гегогп оЬ; ) // Продемонатрировать переопределение метода в обобщенном классе. с1аяв Очеггьбеоещо ( ягагьа чогб Ма1п() ( Создать объект класса бел с параметрам типа ьпг. беп<1пг> ТОЬ = лен беп<ьпг>(88); // Здесь вызывается вариант метода бегОЬ() из класса беп. Сопяо1е.игггеггпе(1ОЬ.бегОЬ())) // й теперь создать объект класса беп2 и присвоить О асылку на него переменной ТОЬ типа бел<хна>.
ТОЬ = пен беп2<ъпг>(99); О здесь вызывается вариант метода бегОЬ() иэ клааса беп2. Сопяо1е.игггегтпе(гОЬ.бегОЬ()); ) Ниже приведен результат выполнения этой программы. Метод бегОЬ() из класса беп возвращает результат: 88 Метод бегОЬ() из класса беп2 возвращает результат: 99 Как следует из результата выполнения приведенной выше программы, переопределяемый вариант метода бегОЬ () вызывается для объекта типа беп2, а его вариант из базового класса вызывается для объекта типа беп.
Обратите внимание на следующую строку кода. гОЬ = лен беп2<гпг>(99); Такое присваивание вполне допустимо, поскольку ТОЬ является переменной типа беп<гпг>. Следовательно, она может ссылаться на любой объект типа беп<1пг> или же объект класса, производного от бел<).
пг>, включая и б ел 2 <1пг >. Разумеется, переменную ТОЬ нельзя использовать, например, для ссылки на объект типа беп2<гпг >, поскольку это может привести к несоответствию типов. Глава 18. Обобщения 625 Перегрузка методов с несколькими параметрами типа Методы, параметры которых объявляются с помощью параметров типа, могут быль перегружены. Но правила их перегрузки упрощаются по сравнению с методами без параметров типа. Как правило, метод, в котором параметр типа служит для указания типа данных параметра этого метода, может быть перегружен при условии, что сигнатуры обоих его вариантов отличаются. Это означает, что оба варианта перегружаемого метода должны отличаться по типу или количеству их параметров.
Но типовые различия должны определяться не по параметру обобщенного типа, а исходя из аргумента типа, подставляемого вместо параметра типа при конструировании обьекта этого типа. Следовательно, метод с параметрами типа может быть перегружен таким образом, что он окажется пригодным не для всех возможных случаев, хотя и будет выглядеть верно. В качестве примера рассмотрим следующий обоб(ценный класс.
Пример неоднозначности, к которой может привести О перегрузка методов с параметрами типа. О Этот код не подлежит коыпиляции. озспо Бузгещ; О Обобщенный класс, содержащий метод БеС(), перегрузка О которого может привести к неоднозначности. с1аав Оеп<Т, Ч> ( т оЫг Ч оЬ2г О О В некоторых случаях зги два метода не будут отличаться своими параметрами типа. рсЫьс чоьв Бег(Т о) ( оЫ=о; ! роЫьс четв Бег(Ч о) ( оЬ2 = ог ) ) с1азв ЛщЬ1оо1Субещо ( всасьс чогг( маьп() ( Оеп<1пС, бопЬ1е> ос = пен Оеп<ьпс, бооЬ1е>() г Оеп<ьпс, 1пс> песок = пен Оеп<ьпс, тпс>(); ок.зеС(10)г // верно, поскольку аргументы типа отличаются поСОК.БеС(10); // неоднозначно, поскольку аргументы ничем не отличаются! ) Рассмотрим приведенный выше код более подробно.
Прежде всего обратите внимание на то, что класс Оеп объявляется с двумя параметрами типа: Т и Ч. В классе Оеп метод Бес () перегружается по параметрам типа т и ч, как показано ниже. 62В часть ). язык с№ роЬ1го чо1О Бес(Т о) ( оЬ1 = аг ) роЬ1ьс чогг) Бес(Ч о) ( оЬ2 = ог ) Такой подход кажется вполне обоснованным, поскольку типы т и )) ничем внешне не отличаются. Но подобная перегрузка таит в себе потенциальную неоднозначность. При таком объявлении класса Оеп не соблюдается никаких требований к различению типов Т и Ч. Например, нет ничего принципиально неправильного в том, что объект класса сеп будет сконструирован так, как показано ниже. Сев<зов, ьпс> поТОК = пев Оеп<ьоо, ьоо>(); В данном случае оба типа, т и 1г, заменяются типом 1пс. В итоге оба варианта метода оес () оказываются совершенно одинаковыми, что, разумеется, приводит к ошибке. Следовательно, при последующей попытке вызвать метод Бес [) для объекта потОК в методе Маьп () появится сообщение об ошибке вследствие неоднозначности во время компиляции.
Как правило, методы с параметрами типа перегружаются при условии, что объект конструируемого типа не приводит к конфликту. Следует, однако, иметь в виду, что ограничения на типы не учитываются при разрешении конфликтов, возникающих при перегрузке методов. Поэтому ограничения на типы нельзя использовать для исключения неоднозначности. Конструкторы, операторы и индексаторы с параметрами типа могут быть перегружены аналогично конструкторам по тем же самым правилам. Ковариантность и контравариантность в параметрах обобщенного типа В главе 15 ковариантность и контравариантность были рассмотрены в связи с не- обобщенными делегатами. Эта форма ковариантности и контравариантности по- прежнему поддерживается в С)), поскольку она очень полезна.
Но в версии СФ 4.0 возможности ковариантности и контравариантности были расширены до параметров обобщенного типа, применяемых в обобщенных интерфейсах и делегатах. Ковариантность и контравариантность применяется, главным образом, для рационального разрешения особых ситуаций, возникающих в связи с применением обобщенных интерфейсов и делегатов, определенных в среде .НЕТ Ргашезчог)<. И поэтому некоторые интерфейсы и делегаты, определенные в библиотеке, были обновлены, чтобы использовать ковариантносгь и контравариантносп параметров типа.
Разумеется, преимуществами ковариантносги и контравариантности можно также воспользоваться в интерфейсах и делегатах, создаваемых собственными силами. В этом разделе механизмы ковариантносги и контравариантности параметров типа поясняются на конкретных примерах. Применение ковариантности в обобщенном интерфейсе Применительно к обобщенному интерфейсу ковариантность служит средством, разрешающим методу возвращать тип, производный от класса, указанного в параметре типа. В прошлом возвращаемый тип должен был в точности соответствовать Глава 18. Обобщения 627 параметру типа в силу строгой проверки обобщений на соответствие типов. Ковариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность.