Для того чтобы воспользоваться делегатом, необходимо создать его экземпляр и задать имена методов, на которые он будет ссылаться. При вызове экземпляра делегата вызываются все заданные в нем методы.
Делегаты применяются в основном для следующих целей:
□ получения возможности определять вызываемый метод не при компиляции,
а динамически во время выполнения программы;
□ обеспечения связи между объектами по типу «источник — наблюдатель»; □создания универсальных методов, в которые можно передавать другие методы;
□ поддержки механизма обратных вызовов.
Все эти варианты подробно обсуждаются далее. Рассмотрим сначала пример реализации первой из этих целей. В листинге 10.1 объявляется делегат, с помощью которого один и тот же оператор используется для вызова двух разных методов
(С001 и Hack).
Листинг 10.1. Простейшее использование делегата
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System;
namespace ConsoleApplication1
{
delegate void Del(ref string s); // объявление делегата
class Class1
{
|
|
public static void cool(ref string s) // метод 1
{
string temp = "";
for (int i = 0; i < s.Length; ++i)
{
if (s[i] == 'о' || s[i] == 'O')
temp += '0';
else
if (s[i] = '1')
temp += '1';
else
temp += s[i];
}
s = temp;
}
public static void Hack(ref string s) // метод 2
{
string temp = "";
for (int i = 0; i < s.Length; ++i)
if (i / 2 * 2 == i)
temp += char.ToUpper(s[i]);
else temp += s[i];
s = temp;
}
static void Main()
{
string s = "cool hackers";
Del d; // экземпляр делегата
for (int i = 0; i < 2; ++i)
{
d = new Del(C001); // инициализация методом 1
if (i == 1) d = new Del(Hack); // инициализация методом 2
d(ref s); // использование делегата для вызова методов
Console.WriteLine(s);
}
}
}
}
Результат работы программы:
c00l hackers
С001 hAcKeRs
Использование делегата имеет тот же синтаксис, что и вызов метода. Если делегат хранит ссылки на несколько методов, они вызываются последовательно в том порядке, в котором были добавлены в делегат. Добавление метода в список выполняется либо с помощью метода Combine, унаследованного от класса System.Delegate, либо, что удобнее, с помощью перегруженной операции сложения. Вот как выглядит измененный метод Main из предыдущего листинга, в котором одним вызовом делегата выполняется преобразование исходной строки сразу двумя методами:
static void Main()
{
string s = "cool hackers";
Del d = new Del(C001);
d += new Del(Hack); // добавление метода в делегат
d(ref s);
Console.WriteLine(s); // результат: С001 hAcKeRs
}
При вызове последовательности методов с помощью делегата необходимо учитывать следующее:
□ сигнатура методов должна в точности соответствовать делегату;
□ методы могут быть как статическими, так и обычными методами класса;
□ каждому методу в списке передается один и тот же набор параметров;
□ если параметр передается по ссылке, изменения параметра в одном методе от
разятся на его значении при вызове следующего метода;
|
|
□ если параметр передается с ключевым словом out или метод возвращает значение, результатом выполнения делегата является значение, сформированное
последним из методов списка (в связи с этим рекомендуется формировать списки только из делегатов, имеющих возвращаемое значение типа void);
□ если в процессе работы метода возникло исключение, не обработанное в том
же методе, последующие методы в списке не выполняются, а происходит по
иск обработчиков в объемлющих делегат блоках;
□ попытка вызвать делегат, в списке которого нет ни одного метода, вызывает
генерацию исключения System.NullReferenceException.