Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 107
Текст из файла (страница 107)
с1азв Яепегтсзоешо ( вгаСТс чогб Ма1п() ( // Создать переменную ссылки на объект Яеп типа Тпг. Оеп<апг> ТОЬ) // создать объект типа Яеп<тпс> и присвоить ссылку // на нето переменной 1ОЬ. ТОЬ = пеи Оеп<апс>(102); // Показать тип данных, хранящихся в переменной ТОЬ.
ТОЬ.ЯЬоиТуре(); // Получить значение переменной 1ОЬ. Тпг ч = ТОЬ.6еСОЬ()' Сопво1е.иг1Сестпе("Значение: " + ч)г Сопво1е.игагестпе()) // Создать объект типа Оеп для строк. Яеп<зггапд> вггОЬ пеи Оеп<вггапд>( "Обобщения повышают эффективность."); // Показать тип данных, хранящихся в переменной всгОЬ. зсгоь.яьоитуре(); // Получить значение переменной вггОЬ. вггтпо згг = вггОЬ.6еСОЬ()( Сопзо1е.нг1гес1пе("Значение: " + зог); ) ) Эта программа дает следукнний результат: к типу т относится яувсеш.тпс32 Значение: 102 К типу Т относится Яувгеш.зггспц Значение: Обобщения повышают эффективность.
Внимательно проанализируем зту программу. Прежде всего обратите внимание на объявление класса 6еп в приведенной ниже строке кода. с1авв 6еп<Т> ( 560 часть!, язык Сз где Т вЂ” это имя параметра типа. Это имя служит в качестве метки-заполнителя конкретного типа, который указывается при создании объекта класса Оеп. Следовательно, имя т используется в классе Оеп всякий раз, когда требуется параметр типа.
Обратите внимание на то, что имя т заключается в угловые скобки (< >). Этот синтаксис можно обобщитьс всякий раз, когда объявляется параметр типа, он указывается в угловых скобках. А поскольку параметр типа используется в классе Оеп, то такой класс считается обоби(енным. В объявлении класса Оеп можно указывать любое имя параметра типа, но по традиции выбирается имя т. К числу других наиболее употребительных имен параметров типа относятся к/ и е.
Вы, конечно, вольны использовать и более описательные имена, например Туа1пе или Ткну. Но в этом случае первой в имени параметра типа принято указывать прописную букву Т. Далее имя т используется для объявления переменной оЬ, как показано в следующей строке кода: Т оЬ; // объявить переменную типа Т Как пояснялось выше, имя параметра типа Т служит меткой-заполнителем конкретного типа, указываемого при создании объекта класса Оеп.
Поэтому переменная оЬ будет иметь тип, привязываемый к т при получении экземпляра объекта класса Оеп. Так, если вместо т указывается тип зггхпд, то в экземпляре данного объекта переменная оь будет иметь тип эгг1пя. А теперь рассмотрим конструктор класса Оеп: рпЬ11о Оеп(Т о) ( оЬ = о; Как видите, параметр о этого конструктора относится к типу Т. Это означает, что конкретный тип параметра о определяется типом, привязываемым к т при создании объекта класса Оеп. А поскольку параметр о и переменная экземпляра оЬ относятся к типу Т, то после создания объекта класса Оеп их конкретный тип окажется одним и тем же.
С помощью параметра типа Т можно также указывать тип, возвращаемый методом, как показано ниже на примере метода ОеЬОЬ () . рпЬ11о Т Оесоо() ( геспгп оЬ; ) Переменная оЬ также относится к типу Т, поэтому ее тип совпадает с типом, возвращаемым методом ОеГОЬ ( ) . Метод ЗЬонтуре () отображает тип параметра Т, передавая его оператору Ьуреог. Но поскольку реальный тип подставляется вместо т при создании объекта класса Оеп, то оператор Ьуреог получит необходимую информацию о конкретном типе.
В классе Оепегхсэпеюо' демонстрируется применение обобщенного класса Оеп. Сначала в нем создается вариант класса Оеп типа 1пг: Оеп<1пс> 10Ь; Внимательно проанализируем это объявление. Прежде всего обратите внимание на то, что тип 1пс указывается в угловых скобках после имени класса Оеп. В этом случае 1пс служит арЕментом тина, пРивязанным к паРамегРУ типа Т в классе Оеп.
В данном объявлении создается вариант класса Оеп, в котором тип Т заменяется типом 1пг везде, Глава 18. Обобщения 561 где он встречается. Следовательно, после этого объявления Тпс становится типом переменной оЬ и возвращаемым типом метода ОеСОЬ () . В следующей строке кода переменной ТОЬ присваивается ссылка на вариант класса Оеп типа Титл 1ОЬ = веы Оеп<1пг>(102); Обратите внимание на то, что при вызове конструктора класса Оеп указывается также аргумент типа 1пс.
Это необходимо потому, что переменная (в данном случае — 1ОЬ), которой присваивается ссылка, относится к типу Оеп<100>. Поэтому ссылка, возвращаемая оператором пен, также должна относиться к типу цен<100>. В противном случае во время компиляции возникнет ошибка. Например, приведенное ниже присваивание станет причиной ошибки во время компиляции. 1ОЬ = пеы аеп<боыъге>(118.12); // Ошибка( Переменная 1ОЬ относится к типу Оеп<ъпг> и поэтому не может использоваться для ссылки на объект типа Оеп<бооЬ|е>. Такой контроль типов относится к одним из главных преимуществ обобщений, поскольку он обеспечивает типовую безопасность.
Затем в программе отображается тип переменной оЬ в объекте 1ОЬ вЂ” тип эузсеш. Тпс32. Это структура .НЕТ, соответствующая типу Тпс, Далее значение переменной оь получается в следующей строке кода; 1пг я = 1ОЬ.ОеСОЬ()Л Возвращаемым для метода ОеСОЬ () является тип Т, который был заменен на тип 1пг при объявлении переменной ТОЬ, и поэтому метод ОеСОЬ () возвращает значение того же типа Тпю В связи с этим данное значение может быть присвоено переменной ы типа 1пс. Далее в классе Оепегйсэцешо объявляется объект типа Оеп<зсг1пд>. яеп<всгьпд> всгоь = пеы Оеп<всг1вч>("Обобщения повышают эффективность.") В этом объявлении указывается аргумент типа эсг1пд, поэтому в объекте класса Оеп вместо Т подставляется тип эгг1пд.
В итоге создается вариант класса Оеп типа зггйпгн как демонстрируют остальные строки кода рассматриваемой здесь программы. Прежде чем продолжить изложение, следует дать определение некоторым терминам. Когда для класса Оеп указывается аргумент типа, например Тпг или эгг1по, то создается так назыэаемый в С№ закрыто сконструированный тип.
В частности, Оеп<1пг> является закрыто сконструированным типом. Ведь по существу такой обобщенный тип, как Оеп<Т>, является абстракцией. И только после того, как будет сконструирован конкретный вариант, например Оеп<1пг>, создастся конкретный тип. В С№ конструкция, подобная Оеп<Т>, называется открыто сконсгпруироеанньгм типом, поскольку в ней указывается параметр типа Т, а не такой конкретный тип, как 1пс. В языке С№ чаще определяются такие понятия, как открытый и закрытый типы.
Открытым типом считается такой параметр типа или любой обобщенный тип, для которого аргумент типа является параметром типа или же включает его в себя. А любой тип, не относящийся к открытому считается закрытым. Сконструированным типом считается такой обобщенный тип, для которого предоставлены все аргументы типов. Если все эти аргументы относятся к закрытым типам, то такой тип считается закрыто сконструированным. А если один или несколько аргументов типа относятся к открытым типам, то такой тип считается открыто сконструированным.
562 Часть ). язык Сз Различение обобщенных типов по аргументам типа Что касается обобщенных типов, то следует иметь в виду, что ссылка на один конкретный вариант обобщенного типа не совпадает по типу с другим вариантом того же самого обобщенного типа. Так, если ввести в приведенную выше программу следующую строку кода, то она не будет скомпилирована: 10Ь = зкгбпт // Неверно! Несмотря на то что обе переменные, 1оь и зсгоь, относятся к типу аеп<т>, они ссылаются на разные типы, поскольку у них разные аргументы.
Повышение типовой безопасности с помощью обсчщений В связи с изложенным выше возникает следующий резонный вопрос: если аналогичные функциональные возможности обобщенного класса оеп можно получить и без обобщений, просто указав объект как тип данных и выполнив надлежащее приведение типов, то какая польза от того, что класс оеп делается обобщенным? Ответ на этот вопрос заключается в том, что обобщения автоматически обеспечивают типовую безопасность всех операций, затрагивающих класс оеп. В ходе выполнения этих операций обобщения исключают необходимость обращаться к приведению типов и проверять соответствие типов в коде вручную.
Для того чтобы стали более понятными преимущества обобщений, рассмотрим сначала программу, в которой создается необобщенный аналог класса оеп. // Класс Нопцеп является полным Функциональным аналогом // класса оеп, но без обобшений. из1пп Зузсеит с1азз Ноппеп ( оЬ)еск оЬ; // переменная оЬ теперь относится к типу оЬ)ест // Передать конструктору ссылку на объект типа оЬ)ест. рцЬ11с Нопоеп(опзесп о) ( оЬ = о; ) // Возвратить объект типа оЬ)еск.