Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Следует особо подчеркнуть, что один и тот же делегат может быть использовандля вызова разных методов во время выполнения программы, для чего достаточноизменить метод, на который ссылается делегат. Таким образом, метод, вызываемыйделегатом, определяется во время выполнения, а не в процессе компиляции. В этом,собственно, и заключается главное преимущество делегата.
ПРИМЕЧАНИЕЕсли у вас имеется опыт программирования на C/C++, то вам полезно будет знать, чтоделегат в C# подобен указателю на функцию в C/C++.
Тип делегата объявляется с помощью ключевого слова delegate. Ниже приведенаобщая форма объявления делегата:delegate возвращаемый_тип имя(список_параметров);
где возвращаемыйтип обозначает тип значения, возвращаемого методами, которыебудут вызываться делегатом; имя — конкретное имя делегата; списокпараметров —параметры, необходимые для методов, вызываемых делегатом. Как только будет создан экземпляр делегата, он может вызывать и ссылаться на те методы, возвращаемыйтип и параметры которых соответствуют указанным в объявлении делегата.
Самое главное, что делегат может служить для вызова любого метода с соответствующей сигнатурой и возвращаемым типом. Более того, вызываемый метод может бытьметодом экземпляра, связанным с отдельным объектом, или же статическим методом,связанным с конкретным классом. Значение имеет лишь одно: возвращаемый типи сигнатура метода должны быть согласованы с теми, которые указаны в объявленииделегата.
Для того чтобы показать делегат в действии, рассмотрим для начала простой пример его применения.// Простой пример применения делегата.using System;// Объявить тип делегата.delegate string StrMod(string str);class DelegateTest { // Заменить пробелы дефисами. static string ReplaceSpaces(string s) { Console.WriteLine("Замена пробелов дефисами."); return s.Replace(' ', '-'); } // Удалить пробелы. static string RemoveSpaces(string s) { string temp = ""; int i; Console.WriteLine("Удаление пробелов."); for(i=0; i < s.Length; i++) if(s[i] != ' ') temp += s[i]; return temp; } // Обратить строку. static string Reverse(string s) { string temp = ""; int i, j; Console.WriteLine("Обращение строки."); for(j=0, i=s.Length-1; i >= 0; i--, j++) temp += s[i]; return temp; } static void Main() { // Сконструировать делегат. StrMod strOp = new StrMod(ReplaceSpaces); string str; // Вызвать методы с помощью делегата. str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = new StrMod(RemoveSpaces); str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = new StrMod(Reverse); str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); }}
Вот к какому результату приводит выполнение этого кода.Замена пробелов дефисами.Результирующая строка: Это-простой-тест.Удаление пробелов.Результирующая строка: Этопростойтест.Обращение строки.Результирующая строка: .тсет йотсорп отЭ
Рассмотрим данный пример более подробно. В его коде сначала объявляется делегат StrMod типа string, как показано ниже.delegate string StrMod(string str);
Как видите, делегат StrMod принимает один параметр типа string и возвращаетодно значение того же типа.
Далее в классе DelegateTest объявляются три статических метода с одним параметром типа string и возвращаемым значением того же типа. Следовательно, они соответствуют делегату StrMod. Эти методы видоизменяют строку в той или иной форме. Обратите внимание на то, что в методе ReplaceSpaces() для замены пробеловдефисами используется один из методов типа string — Replace().
В методе Main() создается переменная экземпляра strOp ссылочного типа StrModи затем ей присваивается ссылка на метод ReplaceSpaces(). Обратите особое внимание на следующую строку кода.StrMod strOp = new StrMod(ReplaceSpaces);
В этой строке метод ReplaceSpaces() передается в качестве параметра. При этомуказывается только его имя, но не параметры. Данный пример можно обобщить: приполучении экземпляра делегата достаточно указать только имя метода, на которыйдолжен ссылаться делегат. Ясно, что сигнатура метода должна совпадать с той, чтоуказана в объявлении делегата. В противном случае во время компиляции возникнетошибка.
Далее метод ReplaceSpaces() вызывается с помощью экземпляра делегата strOp,как показано ниже.str = strOp("Это простой тест.");
Экземпляр делегата strOp ссылается на метод ReplaceSpaces(), и поэтому вызывается именно этот метод.
Затем экземпляру делегата strOp присваивается ссылка на метод RemoveSpaces(),и с его помощью вновь вызывается указанный метод — на этот раз RemoveSpaces().И наконец, экземпляру делегата strOp присваивается ссылка на метод Reverse().А в итоге вызывается именно этот метод.
Главный вывод из данного примера заключается в следующем: в тот момент, когдапроисходит обращение к экземпляру делегата strOp, вызывается метод, на которыйон ссылается. Следовательно, вызов метода разрешается во время выполнения, а не впроцессе компиляции.Групповое преобразование делегируемых методов
Еще в версии C# 2.0 было внедрено специальное средство, существенно упрощающее синтаксис присваивания метода делегату. Это так называемое групповое преобразование методов, позволяющее присвоить имя метода делегату, не прибегая к операторуnew или явному вызову конструктора делегата.
Ниже приведен метод Main() из предыдущего примера, измененный с целью продемонстрировать групповое преобразование методов.static void Main() { // Сконструировать делегат, используя групповое преобразование методов. StrMod strOp = ReplaceSpaces; // использовать групповое преобразование методов string str; // Вызвать методы с помощью делегата, str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = RemoveSpaces; // использовать групповое преобразование методов str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = Reverse; // использовать групповое преобразование методов str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine();}
Обратите особое внимание на то, как создается экземпляр делегата strOp и как емуприсваивается метод ReplaceSpaces в следующей строке кода.strOp = RemoveSpaces; // использовать групповое преобразование методов
В этой строке кода имя метода присваивается непосредственно экземпляру делегата strOp, а все заботы по автоматическому преобразованию метода в тип делегата"возлагаются" на средства С#. Этот синтаксис может быть распространен на любуюситуацию, в которой метод присваивается или преобразуется в тип делегата.
Синтаксис группового преобразования методов существенно упрощен по сравнению с прежним подходом к делегированию, поэтому в остальной части книги используется именно он.Применение методов экземпляра в качестве делегатов
В предыдущем примере использовались статические методы, но делегат можетссылаться и на методы экземпляра, хотя для этого требуется ссылка на объект. Так,ниже приведен измененный вариант предыдущего примера, в котором операции состроками инкапсулируются в классе StringOps. Следует заметить, что в данном случае может быть также использован синтаксис группового преобразования методов.// Делегаты могут ссылаться и на методы экземпляра.using System;// Объявить тип делегата.delegate string StrMod(string str);class StringOps { // Заменить пробелы дефисами. public string ReplaceSpaces (string s) { Console.WriteLine("Замена пробелов дефисами."); return s.Replace(' ', '-'); } // Удалить пробелы. public string RemoveSpaces(string s) { string temp = ""; int i; Console.WriteLine("Удаление пробелов."); for(i=0; i < s.Length; i++) if(s[i] != ' ') temp += s[i]; return temp; } // Обратить строку. public string Reverse(string s) { string temp = ""; int i, j; Console.WriteLine("Обращение строки."); for(j=0, i=s.Length-1; i >= 0; i--, j++) temp += s[i]; return temp; }}class DelegateTest { static void Main() { StringOps so = new StringOps(); // создать экземпляр // объекта класса StringOps // Инициализировать делегат. StrMod strOp = so.ReplaceSpaces; string str; // Вызвать методы с помощью делегатов. str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = so.RemoveSpaces; str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine(); strOp = so.Reverse; str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); }}