Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 116
Текст из файла (страница 116)
Это означает, что параметр т, указываемый в объявлении обобщенного производного класса Пеп, ие требуется для указания базового класса Нопбеп и даже ие может в ием использоваться. Следовательно, класс Пеп наследует от класса Мопоеп обычным образом, т.е. без выполнения каких-то особых условий. Переопределение виртуальных методов в обобщенном классе В обобщенном классе виртуальный метод может быть переопределен таким же образом, как и любой другой метод.
В качестве примера рассмотрим следующую программу, в которой переопределяется виртуальный метод ОеГОЬ (): // Пример переопределения виртуального метода в обвешенном классе. пвтпд Бувсевп // Обобщенный базовый класс. с1авв Оеп<Т> ( Глава )8.
Обобщения 607 рготесвеб т оЫ роЫТс Оеп(Т о) ( оЬ = о; // Возвратить значение переменной оЬ. Этот метод // является виртуальньв<. рпЬ11с ч1гсна1 Т беГОЬ() ( Сопяо1е.игтсе("Метод ОеГОЬ() из класса Оеп" + " возвращает результат: "); геснгп оЬ! ) // Класа, производный от класса пеп. В этом классе // переопределяется метод ОеГОЬ(). с1аяв Оеп2<Т>: Оеп<Т> ( рпЫТс 6еп2(Т о) : Ьаяе(о) ( ) // Переопределить метод ОеГОЬ(). рнЫТс онеггьбе Т ОеГОЬ() ( Сопяо1е.игаве("Метод пеГОЬ() из класса 6еп2" ъ " возвращает результат: ")( гевнгп оЬ; ) ! // Продемонстрировать переопределение метода вобобщенном классе.
с1аяя Очеггябепещо ( ятавас чотб паап() ( // Создать объект класса 6еп с параметром типа апв. Оеп<апс> ТОЬ = пен Оеп<тпс>(88); // Здесь вызывается вариант метода 6еГОЬ() // из класса пеп. Сопяо1е.Игавейапе(ТОЬ.6еГОЬ()); // й теперь создать объект класса Оеп2 и присвоить // ссылку на него переменной 10Ь типа пел<тле>. 10Ь = пеи 6еп2<тпв>(99); // Здесь вызывается вариант метода 6еГОЬ() // из класса 6еп2. Сопяо1е .Игьсевапе (10Ь.6еГОЬ () ); Ниже приведен результат выполнения этой программы.
Метод 6еГОЬ() из класса Оеп возвращает результат: 88 Метод ОеГОЬ() иэ класса 6еп2 возвращает результат: 99 608 Часть ). Язык Сх Как следует из результата выполнения приведенной выше программы, переопределяемый вариант метода ПегоЬ () вызывается для объекта типа Оеп2, а его вариант из базового класса вызывается для объекта типа аеп. Обратите внимание на следующую строку кода: 1ОЬ = пен Оеп2<ъпе>(99); Такое присваивание вполне допустимо, поскольку 1ОЬ является переменной типа Пеп<йпс>. Следовательно, она может ссылаться на любой объект типа Пеп<1пс> или же объект класса, производного от Оеп<1пс>, включая и пеп2<1пс>. Разумеется, переменную ъбь нельзя использовать, например, для ссылки на объект типа пеп2<ъпс>, поскольку это может привести к несоответствию типов.
Перегрузка методов с несколькими параметрами типа Методы, параметры которых объявляются с помощью параметров типа, могут быть перегружены. Но правила их перегрузки упрощаются по сравнению с методами без параметров типа. Как правило, метод, в котором параметр типа служит для указания типа данных параметра этого метода, может быть перегружен при условии, что сигнатуры обоих его вариантов отличаются. Это означает, что оба варианта перегружаемого метода должны отличаться по типу или количеству их параметров.
Но типовые различия должны определяться не по параметру обобщенного типа, а исходя из аргумента типа, подставляемого вместо параметра типа при конструировании объекта этого типа. Следовательно, метод с параметрами типа может быть перегружен таким образом, что он окажется пригодным не для всех возможных случаев, хотя и будет выглядеть верно.
В качестве примера рассмотрим следующий обобщенный класс: // Пример неоднозначности, к которой может привести // перегрузка методов с параметрами типа. // // Этот код не подлежит компиляции. озъпЧ зузеещ; // Обобщенный класс, содержащий метод зее(), перегрузка // которого может привести к неоднозначности.
с1авз Оеп<т, Н> ( т оЬ1; Н оЬ2) // // В некоторых случаях зти два метода не будут // отличаться своими параметрами типа. рцЬ11с чоьд зее(т о) ( оЬ1 = о," ) рцЬ11с чогб Эее(Н о) ( оЬ2 = о; ) ) Глава 18. Обобщения 609 с1ааа КюЬ10о1гупеио ( агаеъс чо10 Иаьп() ( Оеп<ъпв, оопЬ1е> ох = пен Оеп<1пе, ОопЬ1е>(); Оеп<ъпв, 1пв> поСОК = пен Оеп<1пс, ъпг>(); ои.зев(10); // верно, поскольку аргументы типа отличаются ловок.эег(10); // неоднозначно, поскольку аргументы // ничем не отличаются! Рассмотрим приведенный выше код более подробно. Прежде всего обратите внимание на то, что класс Оеп объявляется с двумя параметрами типа: Т и ч.
В классе Оеп метод Бес () перегружается по параметрам типа Т и Ч, как показано ниже. рпЬ11с чо1с( БеС(т о) ( оЬ1 = о1 ) роЬ11с чо1О Бео(Ч о) ( оЬ2 = о; ) Такой подход кажется вполне обоснованным, поскольку типы Т и у ничем внешне не отличаются. Но подобная перегрузка таит в себе потенциальную неоднозначность. При таком объявлении класса Оеп не соблюдается никаких требований к различению типов Т и ч. Например, нет ничего принципиально неправильного в том, что объект класса Оеп будет сконструирован так, как показано ниже.
Оеп<ъпв, ъпе> поТОК = пеи Оеп<ъпв, Тпв>(); В данном случае оба типа„Т и )/ заменяются типом 1пс. В итоге оба варианта метода Бес ( ) оказываются совершенно одинаковыми, что, разумеется, приводит к ошибке. Следовательно, при последующей попытке вызвать метод Бес () для объекта посох в методе ма1п () появит- В ся сообщение об ошибке вследствие неоднозначности во время компиляции.
Как правило, методы с параметрами типа перегружаются при условии, что объект конструируемого типа не приводит к конфликту. Следует, однако, иметь в виду, что ограничения на типы не учитываются при разрешении конфликтов, возникающих при перегрузке методов. Поэтому ограничения на типы нельзя использовать для исключения неоднозначности. Конструкторы, операторы и индексаторы с параметрами типа могут быть перегружены аналогично конструкторам по тем же самым правилам. Получение экземпляров обьектов обобщенных типов Когда приходится иметь дело с обобщениями, то нередко возникает вопрос: не приведет ли применение обобщенного класса к неоправданному раздуванию кода? Ответ на этот вопрос прост: не приведет.
Дело в том, что в С№ обобщения реализованы весьма эффективным образом: новые объектьг конструируемого типа создаются лишь по мере надобности. Этот процесс описывается ниже. Когда обобщенный класс компилируется в псевдокод МОП., он сохраняет все свои параметры типа в их обобщенной форме. А когда конкретный экземпляр класса потребуется 610 Часть Я Язык С№ во время выполнения программы, то ПТ-компилятор сконструирует конкретный вариант этого класса в исполняемом коде, в котором параметры типа заменяются аргументами типа. В каждом экземпляре с теми же самыми аргументами типа будет использоваться один и тот же вариант данного класса в исполняемом коде.
Так, если имеется некоторый обобщенный класс пеп<т>, то во всех объектах типа пеп<т> будет использоваться один и тот же исполняемый код данного класса Следовательно, раздувание кода исключается благодаря тому, что в программе создаются только те варианты класса, которые действительно требуются. Когда же возникает потребность сконструировать объект другого типа, то компилируется новый вариант класса в исполняемом коде. Как правило, новый исполняемый вариант обобщенного класса создается для каждого объекта конструируемого типа, в котором аргумент имеет тип значения, например ъпе или попп1е.
Следовательно, в каждом объекте типа пеп<зпс> будет использоваться один исполняемый вариант класса поп, а в каждом объекте типа пеп<попо1е> — другой вариант класса пеп, причем каждый вариант приспосабливается к конкретному типу значения. Но во всех случаях, когда аргумент оказывается ссылочного типа, используется только один вариант обобщенного класса, поскольку все ссылки имеют пдинаковую длину (в байтах). Такая оптимизация также исключает раздувание кода. Некоторые ограничения, присущие обобщениям Ниже перечислен ряд ограничений, которые следует иметь в виду при использовании обобщений. ° Свойства, операторы, индексаторы и события не могут быть обобщенными.
Но эти элементы могут использоваться в обобщенном классе, причем с параметрами обобщенного типа этого класса. ° К обобщенному методу нельзя применять модификатор ехсегп. 1 ° Типы указателей нельзя использовать в аргументах типа. ° Если обобщенный класс содержит поле типа зсасъс, то в объекте каждого конструируемого типа должна быть своя копия этого поля. Это означает, что во всех экземплярах объектов одного конструируемого типа совместно используется одно и то же поле типа зевса с. Но в экземплярах объектов другого конструируемого типа совместно используется другая копия этого поля. Следовательно, поле типа эеаслс не может совместно использоваться объектами всех конструируемых типов.