Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 114
Текст из файла (страница 114)
// Вариант примера из главы 15, демонстрирующего // .НЕТ-совместимое событие. Переделан с целью показать // применение обобщенного делегата. цзьпд Бузгеш; // Объявить класс, производный от класса ЕчепСАгдз. с1азз Мувчепглгдз: Ечепелгдз ( рць11с 1пс Ечепгицщ/ ) // Объявить тип делегата для события. бе1едасе чосб муечепснапб1ег<т, ч>(т зоцгсе, ч агдз) // Объявить класс, содержащий событие. Глава 18. Обобщения 595 с1азз МуЕчепС ( ягагас Тпг соцпС = О; рць11с ечепс муечепснапб1ег<муечепс, муечепсагдз> Яомевчепгг // Этот метод запускает событие ЯоиеЕчепС.
рцЫТс чоаб опЯоыевчепС() ( МуЕчепСАгдя агд = пеи МуЕчепСАгдз(); 11(яоыенчепг != пц11) ( агд.ечепсицт = соопсьт) яоыевчепС(СЫя, агд)) ) ) с1азз Х. ( рпЫ1с чо1Н Напс)1ег<Т, Ч> (Т зоогсе, Ч агд) мпеге Ч: МуЕчепСАгдз ( Сопзо1е.Игагеьапе("Событие " + агд.кчепгноы + " получено объектом класса Х."); Сопзо1е.иг1Севапе("Источник: " + зоцгсе)) Сопзо1е.игагеЬТпе()я ) ) с1азв у ( риЫТс чогб Напб1ег<Т,Ч>(Т зоцгсе, ч агд) вьете Ч: МуЕчепСАгдз ( сопзо1е.иг1сееьпе("событие " + агд.ечепснци + " получено объектом класса Х.")) Сопзо1е.иг1Сеь1пе("Источникя " + зоцгсе)) Сопзо1е.Игагег Тле()) с1азз пзепепегасЕчепгпе1едаге ( згагас чо1б Маап() ( Х оЫ = пеи Х(); у оЬ2 = пем ТО) МуЕчепС ечС = пем МуЕчепС()) // добавить обработчик Напб1ег() в цепочку событий.
ечг.зотеЕчепС += оЫ .Нап61ег) ечс.яоыеечепс += оь2.напб1ег) // Запустить событие. ечс . ОпзоыеБчепг () ) ечг.опзотевчепС()) ) Вот к какому результату приводит выполнение этой программы; 596 Часть ). Язык С№ Событие О получено объектом класса Х Источник: МуЕчепг Событие О получено объектом класса у Источник: МуЕчепС Событие 1 получено объектом класса Х Источник: МуЕчепг Событие 1 получено объектом класса У Источник: МуЕчепг Обобщенные интерфейсы Помимо обобщенных классов и методов, в С№ допускаются обобщенные интерфейсы. Такие интерфейсы указываются аналогично обобщенным классам.
Ниже приведен переделанный вариант примера из главы, демонстрирующего интерфейс 1Яег1ея. 1Напомним, что 1Яегфез является интерфейсом для класса, генерирующего последовательный ряд числовых значений.) Тип данных, которым оперирует этот интерфейс, теперь определяется параметром типа. // Продемонстрировать применение обобщенного интерфейса. пятно Яуягего рпЫ1с ьпгеггасе 1яег1ея<Т> ( Т Вегыехг()т// возвратить следующее по порядку число чо1б Кеяег()) // генерировать ряд последовательных чисел с самого начала чогб ЯеСЯСагС(Т ч); // задать начальное значение // Реализовать интерфейс 1эег1ея.
с1аяя ВуТиоя<Т>: 1Яегтея<Т> ( Т ягагС) т ча1; // Этот делегат определяет форму метода, вызываемого // для генерирования очередного элемента в ряду // последовательных значений. рпЫьс бе1едаге т 1псВутио(т ч)) // Этой ссылке на делегат будет присвоен метол, // передаваеыый конструктору класса ВуТноя.
1псВуТмо тпсг( рпЫтс ВуТмоя(1псВуТио 1псгМеСЫ ( ясагс = бетап1с(т)( ча1 = бетап1С(Т)) 1псг = ТпсгмеСЫ ) рпЫТс Т Оегыехг() ( ча1 = 1псг(ча1)) гегпгп ча1) Глава (8. Обобщения 59л рпЬ11с «оаб ВезеС() ( ча1 = згагг) рпЫ1с чойб ЯеСЯСагг(Т «) ( ясагс = ч; ча1 = згагг) ) с1азз ТЬгееО ( рпЫ1с гпС х, у, в) рпЫ1с ТпгееО(1пг а, апг Ь, 1пг с) ( х=а; у=ь; я=с; ) ) 1 с1азв Оеп1пСТОещо ( // Определить метод увеличения на два каждого // последующего значения типа апс. згаС1с 1пс 1пСР1пзТио(апг ч) ( гесогп ч + 2) ) // Определить метод увеличения на два каждого // последующего значения типа боиь1е. згагас бооЬ1е ОопЬ1еР1озТио(бопЬ1е ч) ( геспгп ч + 2.0) ) // Определить метод увеличения на два каждого // последующего значения координат объекта типа тьгееО.
згагас ТЬгееР ТЬгееОР1пзТио(ТЬгееО ч) ( 11(ч==пп11) гегпгп пеи Тпгеео(0, О, О); е1зе геспгп пеи тьгееР(ч.х + 2, ч.у + 2, ч.г ь 2)) згаС1с чогб Маап() ( // Продемонстрировать генерирование // последовательного ряда значений типа апс. Вутиоз<1пС> апСВТ = пеи ВуТиоз<апг>(1пСР1пзТио); Тот(1пг 1=0; 1 < 51 1++) Сопзо1е.иг1Се(1пСВТ.ОегиехС() + " "); Сопзо1е.игаге11пе()) // Продемонстрировать генерирование // последовательного ряда значений типа бооЫе. 598 Часть 1.
Языг С№ Вутмоз<т(одЬ1е> оЬ1ВТ = пен Вутноя<оопь1е>(оодь1еР1пзтно) ОЬ1вт.зес5сагс(11.4)т Гог(ъпс 1 О) 1 < 5) Тт+) Сопзо1е.иг1се(с)Ь18т.оеснехс() + " ")) Сопяо1е.игъсевьпе() // Продемонстрировать генерирование // послеловательного ряда значений координат объекта типа тЬгеео. ВуТноя<ТЬгеео> ТьгОВТ = пен Вутноя<тпгеео>(тпгееОР1пятно)~ Тпгеео соогот Гог(гпг 1=0) 1 < 5) Тт+) ( соогт( = ТьгОВТ.Сеснехс()т Сопяо1е.нгасе(соого.х + "," + сопят(.г + " ")7 Сопяо1е.нг1се51пе()т ) Этот код выдает следующий результат: 24 6810 13.4 15.4 17.4 19.4 21.4 0,0,0 2,2,2 4,4,4 б,б,б 8,8,8 В данном примере кода имеется ряд любопытных моментов.
Прежде всего обратите внимание на объявление интерфейса 15егъея в следующей строке кода: риЬ11с Тпгеггасе 15егзея<Т> ( Как упоминалось выше, для объявления обобщенного интерфейса используется такой же синтаксис, как и для объявления обобщенного класса. А теперь обратите внимание на следующее объявление класса вутмоя, реализующего интерфейс 15ег1ея: с1аяя ВуТноя<Т>: 15егаея<Т> ( Параметр типа т указывается не только при объявлении класса вутноя, но и при объявлении интерфейса 15ег1ея.
И это очень важно. Ведь класс, реализующий обобщенный вариант интерфейса, сам должен быть обобщенным. Так, приведенное ниже объявление недопустимо, поскольку параметр типа Т не определен. с1аяз ВуТноз: 15егаез<Т> ( // Неверно! Аргумент типа, требующийся для интерфейса 15егъея, должен быть передан классу ВуТмоз. В противном случае интерфейс никак не сможет получить аргумент типа. Далее переменные, хранящие текущее значение в последовательном ряду (ча1) и его начальное значение (зсагс), объявляются как объекты обобщенного типа т. После этого объявляется делегат 1псВуТно. Этот делегат опрздаляет форму метода, используемого для Глава 18, Обобщения 599 увеличения на два значения, хранящегося в объекте типа Т.
Для того чтобы в классе ВуТиоз могли обрабатываться данные любого типа, необходимо каким-то образом определить порядок увеличения на два значения каждого типа данных. Для этого конструктору класса ВуТиоэ передается ссылка на метод, выполняющий увеличение на два. Эта ссылка хранится в переменной экземпляра Тпсг делегата. Когда требуется сгенерировать следующий элемент в псследовательном ряду, этот метод вызывается с помощью экземпляра 1псг делегата. А теперь обратите внимание на класс Тпгее0. В этом классе инкапсулируются координаты трехмерного пространства (Х,а,'г').
Его назначение — продемонстрировать обработку данных типа класса в классе Вутиоз. Далее в классе Оеп10010ещо объявляются три метода увеличения на два для объектов типа 1пг, боп01е и Тпгееп. Все зти методы передаются конструктору класса ВуТиов при создании объектов соответствующих типов. Обратите особое внимание на приведенный ниже метод ТпгееОР1пзтио () . // Определить метод увеличения на два каждого // последующего значения координат объекта типа тпгееО.
всасъс ТлгееО ТьгееОР1ивтно(тлгееО ч) ( 1<(ч==по11) геспгп пеи ТьгееО(0, О, О); е1ве гесогп пеи Тьгее0(ч.х + 2, ч.у + 2, ч.г ь 2); В этом методе сначала проверяется, содержит ли переменная экземпляра ч пустое значение (пп11). Если она содержит это значение, то метод возвращает новый объект типа Тпгееп со всеми обнуленными полями координат. Ведь дело в том, что переменной ч по умолчанию присваивается значение типа г(егап10 (Т) в конструкторе класса ВуТиоз. Это значение оказывается по умолчанию нулевым для типов значений и пустым для типов ссылок на объекты. Поэтому если предварительно не был вызван метод Весзсагс (), то перед первым увеличением на два переменная ч будет содержать пустое значение вместо ссылки на объект.
Это означает, что для первого увеличения на два требуется новый объект. На параметр типа в обобщенном интерфейсе могут накладываться ограничения таким же образом, как и в обобщенном классе. В качестве примера ниже приведен вариант объявления интерфейса 1Яег1ез с ограничением на использование только ссылочных типов. рпп11с 1пгеггасе 1яеггев<т> ипеге т: с1ава ( Если реализуется именно такой вариант интерфейса 1зегуеэ, в реализующем его классе следует указать то же самое ограничение на параметр типа Т, как показано ниже.
с1авв ВуТнов<Т>: 1зегаев<Т> иьеге Т: с1авв ( В силу ограничения ссылочного типа этот вариант интерфейса 1зег1ез нельзя применять к типам значений. Поэтому если реализовать его в рассматриваемом здесь примере программы, то допустимым окажется только объявление Вутиоз<тпгееп>, но не объявления ВуТмоз<1пс> и ВуТиоз<г)опО1е>. Сравнение экземпляров параметра типа Иногда возникает потребность сравнить два экземпляра параметра типа.
Допустим, что требуется написать обобщенный метод 1а1п (), возвращающий логическое значение Сгпе, если в массиве содержится некоторое значение. Для этой цели сначала можно попробовать сделать следующее: 600 часть !. азы! Сз // Не годится! рпь11с ягагъс ьоо1 Хя1п<т>(т ипат, т(] оья) ( Гогеась(т т 1п оЬя) 11(т == ипат) // Ошибка! гегпгп ггпет геспгп га1яе! ) К сожалению, зта попытка не пройдет. Ведь параметр Т относится к обобщенному типу, и поэтому компилятору не удастся выяснить, как сравнивать два объекта.