C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
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. Эти методы видоизменяют строку в той или иной форме. Обратите внимание на то, что в методе Rep la се Spaces () для замены пробелов дефисами используется один из методов типа 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 и как ему присваивается метод Rep la се Spaces в следующей строке кода.
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 StringOpsO; // создать экземпляр
// объекта класса 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);
}
}
Результат выполнения этого кода получается таким же, как и в предыдущем примере, но на этот раз делегат обращается к методам по ссылке на экземпляр объекта класса StringOps.
Групповая адресация
Одним из самых примечательных свойств делегата является поддержка групповой адресации. Попросту говоря, групповая адресация — это возможность создать список, или цепочку вызовов, для методов, которые вызываются автоматически при обращении к делегату. Создать такую цепочку нетрудно. Для этого достаточно получить экземпляр делегата, а затем добавить методы в цепочку с помощью оператора + или +=. Для удаления метода из цепочки служит оператор - или -=. Если делегат возвращает значение, то им становится значение, возвращаемое последним методом в списке вызовов. Поэтому делегат, в котором используется групповая адресация, обычно имеет возвращаемый тип void.
Ниже приведен пример групповой адресации. Это переработанный вариант предыдущих примеров, в котором тип значений, возвращаемых методами манипулирования строками, изменен на void, а для возврата измененной строки в вызывающую часть кода служит параметр типа ref. Благодаря этому методы оказываются более приспособленными для групповой адресации.
// Продемонстрировать групповую адресацию.
using System;
// Объявить тип делегата.