Г. Шилдт - С#4.0 Полное руководство (1160795), страница 91
Текст из файла (страница 91)
Следует заметить, что в данном случае может быть также использован синтаксис группового преобразования методов. Делегаты могут ссылаться и на методы экземпляра. овгпд Яувгеш," Объявить тип делегата. бе1еоате всггпч Ясгноб(всг1по зсг) с1авв Ясгтпоорв ( /Г Заменить пробелы дефисами. роЫго всг1пд Кер1асеярасев(всг1пч в) ( Сопво1е.Хггсетьпе("Замена пробелов дефисами.") гесигп в.рер1асе(' ', ' †'); ) Удалить пробелы.
рпЫгс всг1пд яешоуезрасев(всгтпо з) всг1по Гешр = 1пс Сопво1е.иг1севьпе("Удаление пробелов.") Рог(1=0( г < в.ьепдгю 1++) 11(в(1) != ' ') Сешр += в(1); 478 Часть!. Язык С№ гегцгп гещр; ) // Обратить строку. рц)з11с яггьпд Веуегяе(ягггпд я) ( ягг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с).
Глава 1б. Делегаты, события и м(мбда-выражения 479 Ниже приведен пример групповой адресации. Это переработанный вариант предыдущих примеров, в котором тип значений, возвращаемых методами манипулирования строками, изменен на чоед, а для возврата измененной строки в вызывающую часть кода служит параметр типа пег. Благодаря этому методы оказываются более приспособленными для групповой адресации.
// Продемонстрировать групповую адресацию. пягпо Яуягею) // Объявить тип делегата. де1едаге чогд ЯсгМод(геГ зсг1пд зсг); с1аяя Мц1ГЬСаяпоещо ( О Заменить пробелы дефисами. зпаг1с чогд Нер1асезрасез(гет ясггпд я) ( Сопяо1е.иг1сеЬ1пе("Замена пробелов дефисами.")у я = з.кер1асе(' ', '-'): // Удалить пробелы. зсас1с чо1д Нещочезрасея(гег ясг1по з) ( ясггпд Сеюр = авг ).у Сопяо1е.иггсеввпе("Удаление пробелов.")у Гог(1=0; г < я.ьепссн; г++) 1Г(я[г) != ' ') Гещр е= я[1); з = сещр; ) // Обратить строку. зсасгс чогд Печегве(гет ягггпд я) ( впг1по Сещр = 1пг Сопзо1е.Хг1геьгпе("Обращение строки."); Бог(1=0, Ь=я.ьепчсп-1; г >= 0; г †, З+Ь) геюр += я[г); з = гещр; ясаг1с чогд магп() ( // Сконструировать делегаты.
ЯсгМод астор; Ясгмод гер1асеЯр = Вер1асезрасея; БСгМод гещочеЯр = Рещоче5расея) БсгМод гечегзе5сг = Нечегяе; ягг1П9 згг = "Это простой тест."; // Организовать групповую адресацию. астор = гер1асеЯр; 480 Часть!. Язык С(( вттор += теуетвезтт; Обратиться х делегату с групповой адресацией. астор(тет втт); сопво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г — объект класса У.
Во благодаря // контравариантности следующая строка кода вполне допустима. СЬапсе1Г сьапое = 1псгА; // Зтот метод возвращает // имеет объект класса Х ясас1с Х 1псгА(Х оЬ)) ( Х Гевр = пен Х() ) Гевр.Ча1 = оЬб.Ча1 + 1; гегпгп'гевр; ) Глава 15. Делегаты, события и лямбда-выражения 481 482 Часть!. Язык С(г х хоь = сьапче(хоЫ1 сопво1е.игьсеь1пе ("хоь: " т хоь.уа1) 1 // В этом случае возвращаемым типом метода 1псгВ служит объект класса Х, /! а возвращаемым типом делегата Сьапэе1С вЂ” объект класса Х. Но благодаря !/ ковариантности следующая строка кода оказывается вполне допустимой. сбапсе = 1псгВ; ХоЬ = (Х) сьапде (ХоЫ; Сопво1е.нгггеъьпе("Хоь.
"" + Хоь.уа1); Вот к какому результату приводит выполнение этого кода. ХоЬ: ХоЬ: В данном примере класс х является производным от класса х. А делегат сьапсе1с объявляется следующим образом. г(е1есаге Х Сьапэе1С (Х оЬ1) Делегат возвращает объект класса х и принимает в качестве параметра объект класса Х. А методы 1псгя () и 1псгВ () объявляются следующим образом.
згасьс Х 1псгя(Х оьЗ) вгаг1с Х 1псгВ(Х оь3) Метод 1псгй ( ) принимает объект класса Х в качестве параметра и возвращает объект того же класса. А метод 1псгВ () принимает в качестве параметра объект класса Х и возвращает объект того же класса. Но благодаря ковариантности и контравариантности любой из этих методов может быть передан делегату Сьапсе1г, что и демонстрирует рассматриваемый здесь пример. Таким образом, в строке Сьапце1С сьапое = 1псгд1 метод 1псг)(() может быть передан делегату благодаря контравариантности, так как объект класса Х служит в качестве параметра метода 1псгя (), а объект класса Х— в качестве параметра делегата сьапсе1с. Но метод и делегат оказываются совместимыми в силу контравариантности, поскольку типом параметра метода, передаваемого делегату, служит класс, являющийся базовым для класса, указываемого в качестве типа параметра делегата. Приведенная ниже строка кода также является вполне допустимой, но на этот раз благодаря ковариантности.
сьапсе = 1псгВ; В данном случае возвращаемым типом для метода 1псгВ () служиХ класс Х, а для делеЬата — класс х. Но поскольку возвращаемый тип метода является производным классом от возвращаемого типа делегата, то оба оказываются совместимыми в силу ковариантности. Глава 15. Делегаты, события и лямбла-выражения 483 Класс Яуа~еаг. Ое1еда~е Все делегаты и классы оказываются производными неявным образом от класса вузсегв. Ое1еоасе.
Как правило, членами этого класса не пользуются непосредственно, и это не делается явным образом в данной книге. Но члены класса г увсет. Ое1есгате могут оказаться полезными в ряде особых случаев. Назначение делегатов В предыдущих примерах был наглядно продемонстрирован внутренний механизм действия делегатов, но эти примеры не показывают их истинное назначение. Как правило, делегаты применяются по двум причинам.