Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » Полное руководство. С# 4.0 - Шилдт Герберт

Полное руководство. С# 4.0 - Шилдт Герберт

Читать онлайн Полное руководство. С# 4.0 - Шилдт Герберт

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 98 99 100 101 102 103 104 105 106 ... 188
Перейти на страницу:

Результат выполнения этого кода получается таким же, как и в предыдущем примере, но на этот раз делегат обращается к методам по ссылке на экземпляр объектакласса StringOps.Групповая адресация

Одним из самых примечательных свойств делегата является поддержка групповойадресации. Попросту говоря, групповая адресация — это возможность создать список,или цепочку вызовов, для методов, которые вызываются автоматически при обращениик делегату. Создать такую цепочку нетрудно. Для этого достаточно получить экземпляр делегата, а затем добавить методы в цепочку с помощью оператора + или +=.Для удаления метода из цепочки служит оператор - или -=. Если делегат возвращаетзначение, то им становится значение, возвращаемое последним методом в списке вызовов. Поэтому делегат, в котором используется групповая адресация, обычно имеетвозвращаемый тип void.

Ниже приведен пример групповой адресации. Это переработанный вариантпредыдущих примеров, в котором тип значений, возвращаемых методами манипулирования строками, изменен на void, а для возврата измененной строки в вызывающуючасть кода служит параметр типа ref. Благодаря этому методы оказываются болееприспособленными для групповой адресации.// Продемонстрировать групповую адресацию.using System;// Объявить тип делегата.delegate void StrMod(ref string str);class MultiCastDemo { // Заменить пробелы дефисами. static void ReplaceSpaces(ref string s) { Console.WriteLine("Замена пробелов дефисами."); s = s.Replace(' ', '-'); } // Удалить пробелы. static void RemoveSpaces(ref string s) { string temp = ""; int i; Console.WriteLine("Удаление пробелов."); for(i=0; i < s.Length; i++) if(s[i] != ' ') temp += s[i]; s = temp; } // Обратить строку. static void Reverse(ref string s) { string temp = ""; int i, j; Console.WriteLine("Обращение строки."); for(j=0, i=s.Length-1; i >= 0; i--, j++) temp += s[i]; s = temp; } static void Main() { // Сконструировать делегаты. StrMod strOp; StrMod replaceSp = ReplaceSpaces; StrMod removeSp = RemoveSpaces; StrMod reverseStr = Reverse; string str = "Это простой тест."; // Организовать групповую адресацию. strOp = replaceSp; strOp += reverseStr; // Обратиться к делегату с групповой адресацией. strOp(ref str); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); // Удалить метод замены пробелов и добавить метод удаления пробелов. strOp -= replaceSp; strOp += removeSp; str = "Это простой тест."; // восстановить исходную строку // Обратиться к делегату с групповой адресацией. strOp (ref str); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); }}

Выполнение этого кода приводит к следующему результату.Замена пробелов дефисами.Обращение строки.Результирующая строка: .тсет-йотсорп-отЭОбращение строки.Удаление пробелов.Результирующая строка: .тсетйотсорпотЭ

В методе Main() из рассматриваемого здесь примера кода создаются четыре экземпляра делегата. Первый из них, strOp, является пустым, а три остальных ссылаются наконкретные методы видоизменения строки. Затем организуется групповая адресациядля вызова методов RemoveSpaces() и Reverse(). Это делается в приведенных нижестроках кода.strOp = replaceSp;strOp += reverseStr

Сначала делегату strOp присваивается ссылка replaceSp, а затем с помощью оператора += добавляется ссылка reverseStr. При обращении к делегату strOp вызываются оба метода, заменяя пробелы дефисами и обращая строку, как и показываетприведенный выше результат.

Далее ссылка replaceSp удаляется из цепочки вызовов в следующей строке кода:strOp -= replaceSp;

и добавляется ссылка removeSp в строке кода.strOp += removeSp;

После этого вновь происходит обращение к делегату strOp. На этот раз обращается строка с удаленными пробелами.

Цепочки вызовов являются весьма эффективным механизмом, поскольку они позволяют определить ряд методов, выполняемых единым блоком. Благодаря этомуулучшается структура некоторых видов кода. Кроме того, цепочки вызовов имеют особое значение для обработки событий, как станет ясно в дальнейшем.Ковариантность и контравариантность

Делегаты становятся еще более гибкими средствами программирования благодарядвум свойствам: ковариантности и контравариантности. Как правило, метод, передаваемый делегату, должен иметь такой же возвращаемый тип и сигнатуру, как и делегат.Но в отношении производных типов это правило оказывается не таким строгим благодаря ковариантности и контравариантности. В частности, ковариантность позволяетприсвоить делегату метод, возвращаемым типом которого служит класс, производныйот класса, указываемого в возвращаемом типе делегата. А контравариантность позволяет присвоить делегату метод, типом параметра которого служит класс, являющийсябазовым для класса, указываемого в объявлении делегата.

Ниже приведен пример, демонстрирующий ковариантность и контравариантность.// Продемонстрировать ковариантность и контравариантность.using System;class X { public int Val;}// Класс Y, производный от класса X.class Y : X { }// Этот делегат возвращает объект класса X и// принимает объект класса Y в качестве аргумента.delegate X ChangeIt(Y obj);class CoContraVariance { // Этот метод возвращает объект класса X и // имеет объект класса X в качестве параметра. static X IncrA(X obj) { X temp = new X(); temp.Val = obj.Val + 1; return temp; } // Этот метод возвращает объект класса Y и // имеет объект класса Y в качестве параметра. static Y IncrB(Y obj) { Y temp = new Y(); temp.Val = obj.Val + 1; return temp; } static void Main() { Y Yob = new Y(); // В данном случае параметром метода IncrA является объект класса X, // а параметром делегата ChangeIt — объект класса Y. Но благодаря // контравариантности следующая строка кода вполне допустима. Changelt change = IncrA; X Xob = change(Yob); Console.WriteLine("Xob: " + Xob.Val); // В этом случае возвращаемым типом метода IncrB служит объект класса Y, // а возвращаемым типом делегата ChangeIt — объект класса X. Но благодаря // ковариантности следующая строка кода оказывается вполне допустимой. change = IncrB; Yob = (Y) change (Yob); Console.WriteLine("Yob: " + Yob.Val); }}

Вот к какому результату приводит выполнение этого кода.Xob: 1Yob: 1

В данном примере класс Y является производным от класса X. А делегат ChangeItобъявляется следующим образом.delegate X ChangeIt(Y obj);

Делегат возвращает объект класса X и принимает в качестве параметра объект класса Y. А методы IncrA() и IncrB() объявляются следующим образом.static X IncrA(X obj)static Y IncrB(Y obj)

Метод IncrA() принимает объект класса X в качестве параметра и возвращаетобъект того же класса. А метод IncrB() принимает в качестве параметра объект класса Y и возвращает объект того же класса. Но благодаря ковариантности и контравариантности любой из этих методов может быть передан делегату ChangeIt, что и демонстрирует рассматриваемый здесь пример.

Таким образом, в строкеChangeIt change = IncrA;

метод IncrA() может быть передан делегату благодаря контравариантности, так какобъект класса X служит в качестве параметра метода IncrA(), а объект класса Y —в качестве параметра делегата ChangeIt. Но метод и делегат оказываются совместимыми в силу контравариантности, поскольку типом параметра метода, передаваемогоделегату, служит класс, являющийся базовым для класса, указываемого в качестве типапараметра делегата.

Приведенная ниже строка кода также является вполне допустимой, но на этот разблагодаря ковариантности.change = IncrB;

В данном случае возвращаемым типом для метода IncrB() служит класс Y, а дляделегата — класс X. Но поскольку возвращаемый тип метода является производнымклассом от возвращаемого типа делегата, то оба оказываются совместимыми в силуковариантности.Класс System.Delegate

Все делегаты и классы оказываются производными неявным образом от классаSystem.Delegate. Как правило, членами этого класса не пользуются непосредственно, и это не делается явным образом в данной книге. Но члены класса System.Delegate могут оказаться полезными в ряде особых случаев.Назначение делегатов

1 ... 98 99 100 101 102 103 104 105 106 ... 188
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу Полное руководство. С# 4.0 - Шилдт Герберт.
Комментарии