Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 108
Текст из файла (страница 108)
рцвтзс опзеск ПеСОЫ ) ( кекцгп оЬ; ) // Показать тип переменной оЬ. рцвтзс иоьд Злоытуре() ( сопзо1е.игъсетдпе ("тип переменной оь: " ь оь.оестуре() ) ) ) // Продемонстрировать применение необобщенного класса. с1азз Ноппеппеио ( зкатзс тоид Иазп() ( Нопцеп 10Ьт Глава 18. Обобщения 563 // Создать объект класса Иопбеп. 10Ь = пен Нопбеп(102); // Показать тип данных, храдящнхся в переменной 10Ь. 10Ь. Бпонтуре (); // Получить значение переменной 10Ь. // На этот раз потребуется приведение типов. 1пс я = (1пс) 10Ь.6еГОЬ(); Сопзо1е.иггсеъ1пе("Значение: " ь т); Сопзо1е.иг1се11пе()л // Создать еще один объект класса Иоп6еп и // сохранить строку з переменной 1Г.
Нопбеп зсгОЬ = пен Нопбеп("Тест на необобщенность")~ // Показать тип данных, хранящихся в переменной зсгОЬ. зсгОЬ.БЛонТуре(); // Получить значение переменной зсгОЬ. // И в этом случае требуется приведение типов. Бсг1пп зсг = (зсг1ЛП) зсгОЬ.ПеГОЬ() л сопэо1е.игагетгпе("значение: " + згг); // Этот код компилируется, но он принципиально неверный! 10Ь эггоЬл // Следующая строка кода приводит к исключительной // ситуации зо время выполнения. // я = (Тпс) 10Ь.ОеГОЬ(); // Ошибка при выполнении! ) ) При выполнении этой программы получается следующий результат: Тнп переменной оЬ: Бузсещ.1ЛГ32 Значение: 102 Тнп переменной оЬ: Бузсещ.зсг1пд Значение: Тест на необобщенность Как видите, результат выполнения этой программы аналогичен результату предыдущей программы. В этой программе обращает на себя внимание ряд любопытных моментов.
Прежде всего, тип Т заменен везде, где он встречается в классе нспбеп. Благодаря этому в классе Нопбеп может храниться объект любого типа, как и в обобщенном варианте этого класса. Но такой подход оказывается непригодным по двум причинам. Во-первых, для извлечения хранящихся данных требуется явное приведение типов. И во-вторых, многие ошибки несоответствия типов не могут быть обнаружены вплоть до момента выполнения программы. Рассмотрим каждую из этих причин более подробно.
564 часть ). Язык С» Начнем со следующей строки кода: ьпС ч = (1пг) 1ОЬ.ОеСОЬ()т Теперь возвращаемым типом метода оесОЬ () является оЬбесс, а следовательно, для распаковки значения, возвращаемого методом оеСОЬ (), и его последующего сохранения в переменной ч требуется явное приведение к типу 1пс. Если исключить приведение типов, программа не будет скомпилирована. В обобщенной версии этой программы приведение типов не требовалось, поскольку тип 1пс указывался в качестве аргумента типа при создании объекта 1ОЬ. А в необобщенной версии этой программы потребовалось явное приведение типов. Но это не только неудобно, но и чревато ошибками.
А теперь рассмотрим следующую последовательность кода в конце анализируемой здесь программы: // Этот код компилируется, но он принципиально неверный! 1ОЬ = вггсьт // Следующая строка кода приводит к исключительной // ситуации во время выполнения. // ч = (ъпС) 1ОЬ .СеСОЬ () т // Ошибка при выполнении! В этом коде значение переменной зсгоь присваивается переменной 10ь. Но переменная зсгоЬ ссылается на объект, содержащий символьную строку, а не целое значение.
Такое присваивание оказывается верным с точки зрения синтаксиса, поскольку все ссылки на объекты класса нспоеп одинаковы, а значит, по ссылке на один объект класса нопоеп можно обращаться к любому другому объекту класса нспоеп. Тем не менее такое присваивание неверно с точки зрения семантики, как показывает следующая далее закомментированная строка кода. В этой строке тип, возвращаемый методом оеСОЬ (), приводится к типу 1пС, а затем предпринимается попытка присвоить полученное в итоге значение переменной 1пс. К сожалению, в отсутствие обобщений компилятор не сможет выявить подобную ошибку. Вместо этого возникнет исключительная ситуация во время выполнения, когда будет предпринята попытка приведения к типу 1пс.
Для того чтобы убедиться в этом, удалите символы комментария в начале данной строки кода, скомпилируйте, а затем выполните программу. При ее выполнении возникнет ошибка. Упомянутая выше ситуация не могла бы возникнуть, если бы в программе использовались обобщения. Компилятор выявил бы ошибку в приведенной выше последовательности кода, если бы она была включена в обобщенную версию программы, и сообщил бы об этой ошибке, предотвратив тем самым серьезный сбой, приводящий к исключительной ситуации при выполнении программы. Возможность создавать типизированный код, в котором ошибки несоответствия типов выявляются во время компиляции, является главным преимуществом обобщений.
Несмотря на то что в С(т всегда имелась возможность создавать "обобщенный" код, используя ссылки на объекты, такой код не был типизированным, т.е. не обеспечивал типовую безопасность, а его неправильное применение могло привести к исключительным ситуациям во время выполнения. Подобные ситуации исключаются благодаря обобщениям. По существу, обобщения переводят ошибки при выполнении в разряд ошибок при компиляции. В этом и заключается основная польза от обобщений. В рассматриваемой здесь необобщенной версии программы имеется еще один любопытный момент.
Обратите внимание на то, как тип переменной ОЬ экземпляра класса Нопоеп получается с помощью метода ЗЬоытуре О в следующей строке кода: Глава 18. Обобщения 565 Сопэо1е .Иг1сеьгпе ("Тип переменной оЬ: " + оЬ .Пестуре () ) ) Как пояснялось в главе 11, в классе ОЬ1есс определен ряа методов, доступных для всех типов данных. Одним из иих является метод пестуре (), возвращающий объект класса туре, который описывает тип вызывающего объекта во время выполнения. Следовательно, конкретный тип объекта, иа который ссылается переменная оЬ, становится известным во время выполнения, несмотря иа то, что тип переменной ОЬ указан в исходном коде как ОЬ1есс. Именно поэтому в среде СЕВ будет сгенерировано исключение при попытке произвести неверное приведение типов во время выполнения программы.
Обобщенный класс с двумя параметрами типа В классе обобщенного типа можно указать два или более параметра типа. В этом случае параметры типа указываются списком через запятую. В качестве примера ниже приведеи класс Тноьеп, являющийся вариантом класса беп с двумя параметрами типа. // Простой обобщенный класс с двумя параметрами // типа Т и Ч. пз1пс зуагещ; с1авз тнооеп<т, ч> ( т оь12 Ч оЬ2; // Обратите внимание на то, что в этом конструкторе // указываются параметры типа Т и Ч. ривтъс Тнооеп(Т о1, Ч о2) ( оЬ1 = о1; оЬ2 = о2; ) // Показать типы Т и Ч.
рпвьъс чо1б зьонТурез() ( Сопзо1е.кгъгеъьпе("К типу Т относится " Ь Суреот(Т)); Сопэо1е.кгьгеъъпе("К типу Ч относится " Ь Гуреот(Ч)); ) рпЬ11с Т сегоЬ1() ( геспгп оЬ12 ) рпЬ11с Ч ПеГОЬ)2() ( геспгп оЬ2; ) ) // Продемонстрировать применение обобщенного класса // с двумя параметрами типа. с1аяз 51щроеп ( зсасъс чогб Иа1п() ( 566 Часть(. язык С» тнобеп<ъпс, вгг1пч> ГБОЬ1 = пен Тнобеп<1пс, всгтпч>(119, "Альфа Бета Гамма")г // Показать типы. ГБОЬ1.вьоитурев() // Получить и вывести значения.
Тпс ч = ГЗОЬ9.9есоЬ1()/ Сопво1е.нг1сеътпе("Значение: " + ч); зсгъпч всг = ГБОЬЯ.беГОЬ)2(); Сопво1е.нгагеъьпе("Значение: " + вгг); ) ) Эта программа дает следующий результат: К типу Т относится 5увсеи.1пс32 К типу У относится 5увсев.зсгтпч Значение: 119 Значение: Альфа Бета Гамма Обратите внимание на то, как объявляется класс тиобеп: с1авв Тиобеп<Т, у> ( В этом объявлении указываются два параметра типа т и у, разделенные запятой. А поскольку у класса Тыобеп два параметра типа, то при создании объекта этого класса необходимо указывать два соответствующих аргумента типа, как показано ниже. Тнобеп<1пг, всг1пч> ГЗОЬ9 = пен тнобеп<ъпг, вгг1пч>(119, "Альфа Бета Гака<а")! В данном случае вместо т подставляется тип Тпс, а вместо у — тип всг1пб. В представленном' выше примере указываются аргументы разного типа, но они могут быть и одного типа.