Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 115
Текст из файла (страница 115)
Требуется ли для этого поразрядное сравнение или же только сравнение отдельных полей? А возможно, сравнение ссылок? Вряд ли компилятор сможет найти ответы на эти вопросы. Для сравнения двух объектов параметра обобщенного типа нужно воспользоваться методом Соп!рагеТО (), определенным в 1соирагаЫе — одном из стандартных интерфейсов среды .]ь]ЕТ. Этот интерфейс реализован во всех встроенных в С(т типах, включая Тпс, ясг1по и боиь1е. Его нетрудно реализовать и в создаваемом классе.
В интерфейсе ТсоирагаЬ1е определен только метод Сотрагето (): 1пс Соирагето(оп]есс оЬ]) В методе соирагеТО () вызывающий объект сравнивается с объектом, указываемым в качестве параметра ОЬ1. А в итоге возвращается нулевое значение, если оба объекта имеют одинаковые значения, положительное значение, если вызывающий объект имеет большее значение, чем объект ОЬ1, и отрицательное значение, если вызывающий объект имеет меньшее значение, чем объект ОЬп. Для того чтобы воспользоваться методом Сод!рагеТО (), необходимо сначала наложить ограничение, требующее, чтобы каждый аргумент типа реализовал интерфейс 1сол!рагаЫе. А затем остается только вызвать метод СопрагеТо (), когда потребуется сравнить два экземпляра объектов, определяемых параметрами типа.
В качестве примера ниже приведен исправленный вариант упоминавшегося выше метода 151п ( ) . // Требуется интерфейс 1СоирагаЫе. риь11с ягагъс ьоо1 1ятп<т>(т нпаг, т(] оья) ноете Т: 1СоирагаЫе ( Тогеасп(Т т Тп оЬя) 11(т.соирагето(ипас) == 0) // теперь верно, // поскольку используется метод СоирагеТо() гегпгп ггое) геспгп Га1яет ) Обратите внимание на применение следующего ограничения; нпеге Т: 1соирагаЬ1е Это ограничение гарантирует, что для аргументов, обозначающих типы в методе 1з1п (), допустимы только те типы, в которых реализован интерфейс 1сопрагаЬ1е.
В приведенном ниже примере программы демонстрируется применение метода 1я1п ( ), а также показывается, насколько просто интерфейс 1соп!рагаЫе реализуется в классе. // Продемонстрировать применение интерфейса 1СоирагаЬ1е. пягпд зуягеи! Глава [8. Обобщения 601 с1азз МуС1азз: 1СошрагаЫе ( роЫ1с 1пс Ча14 риЬ11с МуС1азз(апс х) ( Ча1 = х; // Реализовать интерфейс 1СоирагаЬ1е. роЫ1с Тпс СоврагеТо(оЬ]есс оЬ]) ( геспгп На1 — ((МуС1авя) оЬ3) Ла14 с1азя СоирагеРеио [ // Требуется интерфейс 1СоирагаЫе. роы1с ягаггс ьоо1 1з1п<т>(т ньаг, т[] оьз) нпеге Т: 1СоирагаЬ1е ( Тогеасп(Т ч гп оЬз) 11(т.Соирагето(нпас) == О) // теперь верно, // поскольку используется метод СоирагеТо() гегвгп Ггое; гесогп Та1зея ) // Продемонстрировать сравнение.
ясасас тогб Магп() ( // Испольэовать метод 1з1п() вместе с типом гпс. Тпс[] опия = [ 1, 2, 3, 4, 5 )я 11(131п[2, ппиз)) Сопяо1е.иг1ге51пе("Найдено 2."); 11(1в1п(99, поия)) Сопзо1е.нгасебапе("Не подлежит выводу."); // испольэовать метод 1з1п() вместе с типом згггпд. ясг1пц[] зсгя = ( "один", "два", "три")) 11(1з1п("два", зсгз)) Сопяо1е.иггсебапе("Найдено два."); 11(1я1п("пять" ясгя)) Сопяо1е.иг1ге11пе("Не подлежит выводу."); // Использовать метод 1з1п() вместе с // классом МуС1азз.
МуС1аяв[] ися = ( пен МуС1азз(1), пен МуС1азз(2), пен МуС1азя(3), пен МуС1азз(4) ); 11(1з1п(пен МуС1азз(3), всз)] Сопзо1е.нгусейупе("Найдено МуС1аяз(3).")) 602 Часть(. Язык С№ 11 (1в1п (пен мус1ава (99), шов) ) Сопво1е.нг1сеъъпе("Не подлежит выводу.")! Вот к какому результату приводит выполнение этой программы: Найдено 2. Найдено два. Найдено МуС1авв(3) Данная программа в целом верна, но в ней все же имеется одно потенциально слабое место. Обратите внимание на то, как метод сошрагето () реализуется в классе мус1авв: риЬ11с 1пс Сошрагето(обзесс оЬ1) ( гегигп ча1 — ((муС1авв) оЬ1).Ча1; Параметр, сравниваемый в методе СощрагеТо (), должен быть типа оЬбесс, и поэтому объект, определяемый параметром оЬТ, должен быть явно приведен к типу класса Мус1авв, чтобы получить доступ к переменной Ча1.
Но именно подобного рода приведение типов и призваны исключить обобщения( Для выхода из этого затруднительного положения в С№ предоставляется обобщенный вариант интерфейса 1сошрагаЫе, который объявляется следующим образом: рпь11с Тпгеггасе 1сошрагаые<т> В этом варианте тип сравниваемых данных передается в качестве аргумента параметру Т. Вследствие этого объявление метода сощрагеТо () претерпело следующие изменения: 1пс СошрагеТо(Т оЬ№) Теперь в методе СошрагеТо () можно указывать надлежащий тип параметра, а приведение типа оЬб есс к требуемому типу больше не требуется. Обобщенный интерфейс 1СощрагаЬ1е<Т> реализуется также всеми встроенными типами. Ниже приведен улучшенный вариант класса МуС1авв, в котором реализуется обобщенный интерфейс 1СошрагаЫе<Т>.
// В этом варианте класса МуС1авв реализуется обобщенный // интерфейс 1СошрагаЫе<Т>. с1авв МуС1авв: 1СошрагаЫе<МуС1авв> ( рпЫ1с Тпс Ча1; рпЫъс МуС1авв(ъпс х) ( Ча1 = х; ) риЬ11с 1пг Сошрагето(муС1авв оЬ№) ( гегпгп ча1 — оь№.ча1к // Приведение типов больше не требуется. ) ) Как видите приведение типов больше не требуется в следующей строке кода из метода СошрагеТо(): геспгп Ча1 — оЬ).Ча1; // Приведение типов больше не требуется. Параметр типа для интерфейса 1СощрагаЫе относится к классу Мус1авв, и поэтому тип параметра оЬб теперь известен как МуС1азв. Ниже приведен обновленный вариант метода 1в1п (), в котором требуется обобщенный интерфейс 1СощрагаЫе<Т>.
Глава!8. Обобщения 603 // Требуется обобщенный интерфейс 1сощрагаЬ1е<т>. рпьтхс эсас1с ьоо1 1э1п<т>(т ньас, т[] оьэ) ньеге Т: 1Сощрагавае<Т> ( гогеась(Т ч Тп оЬэ) 11(ч.СощрагеТо(ньаС) == О) // теперь верно, // поскольку используется метод СощрагеТо() геспгп Сгпе; гегпгп Та1эег ! На заметку( Если параметр тина обоэиачает ссылку или ограничение на базоеый класс, то к экээмпляраи обьектое, определяемых таким гщоаметром типа, можно применять операторы == и ! =, хотя они проверяют на равенство только ссылки. А для сравнения значений придется реализоеагяь инперфейс гсощрагаЬ1е или же обобщенный интерфейс Тсотра габте <Т>.
Иерархии обобщенных классов Обобщенные классы могут входить в иерархию классов аналогично иеобобщеииым классам. Следовательно, обобщенный класс может действовать как базовый или производный класс. Главное отличие между иерархиями обобщенных и иеобобщеиных классов заключается в том, что в первом случае аргументы типа, необходимые обобщенному базовому классу, должны передаваться всеми производными классами вверх по иерархии аналогично передаче аргументов конструктора. Применение обобщенного базового класса Ниже приведен простой пример иерархии, в которой используется обобщенный базовый класс. // Простая иерархия обобщенных классов. пэ1пд яуэсещ; // Обобщенный базовый класс.
с1аээ реп<1> ( т оь| ров[хо Оеп(Т о) [ оЬ = о' ) // Возвратить значение переменной оЬ. рпЬТ[с Т ПеСОЬ() ( геспгп оЬ) ) ) // Класс, производный от класса Оеп. с1аэв Оеп2<Т>: Оеп<Т> ( рпв[йс Оеп2(Т о) . "Ьаэе(о) ( О ... ) ) 604 Часть (. Язык С» с1азз беппъегоещо ( зсасьс чо1б Иа1П 0 ( беп2<вггьпд> о2 = пеи беп2<зсг1пд>("привет")т Сопзо1е.иг1ге11пе(ц2.беСОЬ()): В этой иерархии класс беп2 наследует от обобщенного класса беп.
Обратите виимание иа объявление класса беп2 в следующей строке кодж с1азз беп2<Т>: беп<Т> ( Параметр типа Т указывается в объявлении класса беп2 и в то же время передастся классу беп. Это означает, что любой тип, передаваемый классу беп2, будет передаваться также классу беп. Например, в следующем объявлении: беп2<зггъпц> о2 = пеи беп2<зсгъпц>("Привет")т параметр типа зсг1по передается классу беп. Поэтому переменная оь в той части класса беп2, которая относится к классу беп, будет иметь тип зтг1пп. Обратите также внимание иа то, что в классе беп2 параметр типа т ие используется, а только передастся вверх по иерархии базовому классу беп. Это означает, что в производном классе следует непременно указывать параметры типа, требующиеся его обобщенному базовому классу, даже если этот производный класс ие обязательно должен быть обобщеииым. Разумеется, в производный класс можно свободно добавлять его собственные параметры типа, если в этом есть потребность.
В качестве примера ниже приведен вариант предыдущей иерархии классов, где в класс беп2 добавлен собствеииый параметр типа. // Пример добавления собственных параметров типа в производный класс. пзъпд зузсещ; // Обобщенный базовый класс. с1азз беп<Т> ( Т оЬ| // объявить переменную типа Т // Передать конструктору ссылку типа Т. рпь11с беп(Т о) ( оЬ = от // Возвратить значение переменной оЬ. рпЬ11с Т бесОЫ) ( геспгп оЬ; ) Класс, производный от класса беп. В этом классе О определяется второй параметр типа Ч. с1азз беп2<Т, Ч>: беп<Т> ( Ч оьзт рпьъъс беп2(т о, Ч о2) : Ьазе(о) ( оЬ2 = о2т ) Глава 18.
Обобщения 605 рпь11с Ч ОеГОЬ92() ( гегигп оЬ2; ) ) // Создать объект класса Оеп2. с1аяя Оепигегоещо2 ( ясас1с чогб Ма).п() ( // Создать объект класса Сеп2 с параметрами // типа ясг1пд и ьпс. Оеп2<ясгьпд, 1пс> х = пен Оеп2<ясг1пд, 1пс>("Значение равно: ", 99); Сопяо1е.нг1ге(х.оеГОЬ!))> Сопяо1е .Иг1геЬ1пе (х . Оесоь)2 () ); ) Обратите внимание на приведенное ниже обьявление класса Оеп2 в данном варианте иерархии классов.
с1аяя Оеп2<Т, Ч> : Оеп<Т> ( В этом объявлении т — зто тип, передаваемый базовому классу Оеп;)/ — тип, характерный только для производного класса Оеп2. Он служит для объявления объекта оЬ2 и в качестве типа, возвращаемого методом ОегОЬп 2 () . В методе Ма1п () создается объект класса Оеп2 с параметром т типа ясг1пд и параметром ч типа 1пс, Поэтому код из приведенного выше примера выдает следующий результат; значение равно: 99 Обобщенный производный класс Пеобобшенный класс может быть вполне законно базовым для обобщенного производного класса. В качестве примера рассмотрим следующую программу: // Пример необобщенного класса в качестве базового для // обобщенного проиэводного класса. пяьпд Буягещ; // Необобщенный базовый класс. с1аяя Нопдеп ( ьпг попы рпвъгс НопОеп(ьпг 1) ( ппщ = 1; ) ровгьс Тпг Оесипщ() [ геспгп попц ) // Обобщенный производный класс.
606 Часть ). Язык Сз с1авв Пеп<т>: Ноппеп ( Т оЬт рпвттс Оеп(т о, 1пс 1) : Ьаве (1) ( оЬ = о; // Возвратить значение переменной оЬ. роЬ11с Т оеГОЬ() ( гесогп оЬт ) ) // Создать объект класса Сел. с1авв Нтегпеиоз ( всас1с чоки Маьп() ( // Создать объект класса Пеп с параметром // типа вггьпд.
Сеп<зсгьпд> н = пеи Пеп<зсгзпо>("Привет", 47) Сопво1е.иг1се(н.оеГОЬ() ь " ")т Сопво1е.ыг1сеъ1пе(н.пекины())) ) Эта программа дает следующий результат: Привет 47 В данной программе обратите внимание иа то, как класс оеп наследует от класса Моппеп в следующем объявлении; с1авв Сеп<Т> т Нопоеп ( Класс Мод пел ие является обобщенным, и поэтому аргумент типа для него ие указывается.