Г. Шилдт - С#4.0 Полное руководство (1160795), страница 111
Текст из файла (страница 111)
Вместо этого возникнет исключительная ситуация во время выполнения, когда будет предпринята попьпка приведения к типу Епг. Для того чтобы убедиться в этом, удалите символы комментария в начале данной строки кода, скомпилируйте, а затем выполните программу. При ее выполнении возникнет ошибка. Упомянутая выше ситуация не могла бы возникнуть, если бы в программе использовались обобщения.
Компилятор выявил бы ошибку в приведенной выше последовательности кода, если бы она была включена в обобщенную версию программы, и сообщил бы об этой ошибке, предотвратив тем самым серьезный сбой, приводящий к исключительной ситуации при выполнении программы. Возможность создавать типизированный код, в котором ошибки несоответствия типов выявляются во время Глава 18. Обобщения 583 компиляции, является главным преимуществом обобщений. Несмотря на то что в С)) всегда имелась возможность создавать "обобщенный" код, используя ссылки на объекты, такой код не был типизированным, т.е.
не обеспечивал типовую безопасность, а его неправильное применение могло привести к исключительным ситуациям во время выполнения. Подобные ситуации исключаются благодаря обобщениям. По существу, обобщения переводят ошибки при выполнении в разряд ошибок при компиляции. В этом и заключается основная польза от обобщений. В рассматриваемой здесь необобщенной версии программы имеется еще один любопытный момент.
Обратите внимание на то, как тип переменной ОЬ экземпляра класса НопСеп создается с помощью метода БЬохтуре () в следующей строке кода. Сопзо1е.Хг1севьпе("Тип переменной оЬ: " ь оЬ.Оегтуре())г Как пояснялось в главе 11, в классе ОЬ> ест определен ряд методов, доступных для всех типов данных.
Одним из них является метод сестуре (), возвращающий объект класса туре, который описывает тип вызывающего объекта во время выполнения. Следовательно, конкретный тип объекта, на который ссылается переменная ОЬ, становится известным во время выполнения, несмотря на то, что тип переменной ОЬ указан в исходном коде как оЬ1 е с с. Именно поэтому в среде СЕВ будет сгенерировано исключение при попытке выполнить неверное приведение типов во время выполнения программы. Обобщенный класс с двумя параметрами типа В классе обобщенного типа можно указать два или более параметра типа. В этом случае параметры типа указываются списком через запятую. В качестве примера ниже приведен класс Тхобеп, являющийся вариантом класса Оеп с двумя параметрами типа. // Простой обобщенный класс с двумя параметрами типа Т и Ч. пз1пэ Бузгею; с1азз Тнобеп<Т, Ч> ( Т оЫ; Ч оЬ2; // Обратите внимание на то, что в этом конструкторе // указываются параметры типа Т и Ч.
роЫьс Тнооеп(Т о1, Ч о2) ( оЫ =о1; оЬ2 = о2; ) // Показать типы Т и Ч. роЫ1с чоьб зпонТурея О ( Сопзо1е.Хгьсеъьпе("К типу Т относится " + Суреот(Т)) 1 Сопзо1е.Хгьгеввпе("К типу Ч относится " + Суреот(Ч))) ) роЬ1ьс Т ЧесоЬ1() ( 584 Часть!. Язык С№ гесогп оЫ; ) роЫгс У ОеСОЬ92 () ( геспгп оЬ2; ) ) Продемонстрировать применение обобщенного класса с двумя параметрами типа. с1авв Ягщроеп ( всасгс но1с( ма1п() ( Тнооеп<гпг, вьггпч> СБОЬ) пен ТноСеп<1пг, вгггпч>(119, "Альфа Бета Гамма" ); l/ Показать типы. СБОЬз.впомтурев()~ // Получить и вывести значения, гпС ч = СБОЬ].чегоЫ ()," Сопво1е.нг1сеь1пе("Значение: " + ч)," вгггпф вгг = СЯОЬ).оеСОЬ12(); Сопво1е.иг1Сеъьпе("Значение: " + вгг); Эта программа дает следующий результат. К типу Т относится Яувгещ.1пС32 К типу У относится Яувгещ.згггпч значение: 119 Значение: Альфа Бета Гамма Обратите внимание на то, как объявляется класс тыопеп.
с1авв Тиосеп<Т, У> ( В этом объявлении указываются два параметра типа т и )г, разделенные запятой. А поскольку у класса тыоОеп два параметра типа, то при создании объекта этого класса необходимо указывать два соответствующих аргумента типа, как показано ниже. ТНОБЕП<1ПС, ВСГГПБ> СдОЬ1 пен Тносеп<гпг, вггтпч>(119, "Альфа Бета Гамма" ); В данном случае вместо т подставляется тип гпС, а вместо Г) — тип ЯСг1по.
В представленном выше примере указываются аргументы разного типа, но они могут быть и одного типа. Например, следующая строка кода считается вполне допустимой. ТНОБЕП<ВСГГПЦ, ВСГГПБ> Х = пен Тнооеп<всг1пц, всг1пч>("Не11о", "БоооЬуе")г В этом случае оба типа, Т и Ч, заменяются одним и тем же типом. в Сг1пд. Ясно, что если бы аргументы были одного и того же типа, то два параметра типа бьгли бы не нужны. Глава 18. Обобщения 585 Общая форма обобщенного класса Синтаксис обобщений, представленных в предыдущих примерах, может быть сведен к общей форме.
Ниже приведена общая форма объявления обобщенного класса. с1вяя имч класса<список параметров типа> ( А вот как выглядит синтаксис объявления ссылки на обобщенный класс. имя класса<список аргументов типа> имв переменной = пеи имл класса<список параметров типа> (списск аргументов конструктора) Ограниченныетипы В предыдущих примерах параметры типа можно было заменить любым типом данных. Например, в следующей строке кода объявляется любой тип, обозначаемый как Т.
с1аяя Оеп<Т> ( Это означает, что вполне допустимо создавать объекты класса Бе п, в которых тип т заменяется типом 1пс, с(оц(>1е, я с ггпсь Г11еБсгеаы или любым другим типом данных. Во многих случаях отсутствие ограничений на указание аргументов типа считается вполне приемлемым, но иногда оказывается полезно ограничить круг типов, которые могут быть указаны в качестве аргумента типа. Допустим, что требуется создать метод, оперирующий содержимым потока, включая объекты типа И1еБСгеаы или МеыогуБСгеаы. На первый взгляд, такая ситуация идеально подходит для применения обобщений, но при этом нужно каким-то образом гарантировать, что в качестве аргументов типа будут использованы только типы потоков, но не Тпс или любой другой тип.
Кроме того, необходимо как-то уведомить компилятор о том, что методы, определяемые в классе потока, будут доступны для применения. Так, в обобщенном коде должно быть каким-то образом известно, что в нем может быть вызван метод йеас( () . Длг( выхода из подобных ситуаций в С(( предусмотрены ограниченные типы.
Указывая параметр типа, можно наложить определенное ограничение на этот параметр. Это делается с помощью оператора и)гете при указании параметра типа: с1аяя имч класса<параметр типа> инесе параметр типа: ограничении ( гг где ограничения указываются списком через запятую. В С() предусмотрен ряд ограничений на типы данных. ° Ограничение на базовым класс, требующее наличия определенного базового класса в аргументе типа. Это ограничение накладывается указанием имени требуемого базового класса. Разновидностью этого ограничения является неприкрытое ограничение типа, при котором на базовый класс указывает параметр типа, а не конкретный тип.
Благодаря этому устанавливается взаимосвязь между двумя параметрами типа. ° Ограничение на интерфеис, требующее реализации одного или нескольких интерфейсов аргументом типа. Это ограничение накладывается указанием имени требуемого интерфейса. 586 Часть ). Язык С№ ° Ограничение на конслгрукпгор, требующее предоставить конструктор без параметров в аргументе типа. Это ограничение накладывается с помощью оператора лен(). ° Ограничение ссылочного пгипа, требующее указывать аргумент ссылочного типа с помощью оператора с1азз.
Ограничение типа значения, требующее указывать аргумент типа значения с помощью оператора зггпсг. Среди всех этих ограничений чаще всего применяются ограничения на базовый класс и интерфейс, хотя все они важны в равной степени. Каждое из этих ограничений рассматривается далее по порядку. Применение ограничения на базовый класс Ограничение на базовый класс позволяет указывать базовый класс, который должен наследоваться аргументом типа. Ограничение на базовый класс служит двум главным целям.
Во-первых, оно позволлет использовать в обобщенном классе те члены базового класса, на которые указывает данное ограничение. Это дает, например, возможность вызвать метод или обратиться к свойству базового класса. В отсутствие ограничения на базовый класс компилятору ничего не известно о типе членов, которые может иметь аргумент типа. Накладывая ограничение на базовый класс, вы тем самым даете компилятору знать, что все аргументы типа будут иметь члены, определенные в этом базовом классе. И во-вторых, ограничение на базовый класс гарантирует использование только тех аргументов типа, которые поддерживают указанный базовый класс. Это означает, что для любого ограничения, накладываемого на базовый класс, аргумент типа должен обозначать сам базовый класс или производный от него класс.
Если же попытаться использовать аргумент типа, не соответствующий указанному базовому классу или не наследующий его, то в результате возникнет ошибка во время компиляции. Ниже приведена общая форма наложения ограничения на базовый класс, в которой используется оператор н)чете: ийеге Т: имя базового класса где т обозначает имя параметра типа, а имя базового класса — конкретное имя ограничиваемого базового класса. Одновременно в этой форме ограничения может быть указан только один базовый класс.