C# → Делегаты

Делегаты – способ именования сигнатуры метода. Делегаты предназначены для передачи методов другим методам. Если требуется передавать методы в качестве параметров, сведения о методе необходимо поместить в новый тип объекта – делегат. Делегаты содержат информацию о методе.

public delegate void VoidOperation(uint x);

Определили делегат VoidOepration и указали, что каждый экземпляр этого делегата может содержать сведения о методе, принимающем один параметр uint и возвращающем void. Синтаксис аналогичен тому, что используется при определении методов, за исключением того, что отсутствует тело метода, а определение предваряется ключевым словом delegate.

Определив делегата, мы можем создать его экземпляр и использовать его для хранения сведений о конкретном методе.

Пример использования:

int x = 40;
GetString first = new GetString(x.ToString);
Console.WriteLine(first()); // эквивалентен WriteLine(x.ToString());

В этом коде мы создали экземпляр делегата типа GetString и инициализировали его таким образом, что он стал ссылаться на метод ToString() целочисельной переменной х. Делегаты в C# всегда синтаксически принимают конструктор с одним параметром, в качестве параметра передается метод на который будет ссылаться делегат. Сигнатура входного метода (параметра) должна соответствовать сигнатуре, указанной при объявлении делегата, иначе возникает ошибка при компиляции.

В любом коде указание имени делегата с последующими скобками, в которых перечислены параметры, имеет точно такое же действие, что и вызов метода, для которого делегат служит оболочкой.

Делегат – это объект, хранящий указатель на функцию (исполнимый код) и способный вызвать его на выполнение. Так бы выглядел код на С++ (void EntryPoint()-) метод, в котором исполняется поток.

Thread NewThread = new Thread();
Thread.Start(EntryPoint);

Объекты делегаты являются наседниками класса System.Delegate, они позволяют менять значение указателя непосредственно во время работы программы (фактически, динамически вызывать разные функции) и способны хранить одновременно несколько указателей на разные функции (+, +=). Компилятор не позволит применить операции (+, +=) для совмещения нескольких методов в одном делегате если метод, на который он ссылается возвращает не значение void.

Currency c = new Currency();
delegates d1 = new delegates(c.fun);
delegates d2 = new delegates(c.fun4);
delegates d3 = new delegates(c.fun4);
d3 = d3 + d1;  // d3 содержит вызовы fun4 + fun
d3 += d2;  // d3 содержит вызовы fun4 + fun + fun4
d3(5, ref x); // произойдет цепочка вызовов

Если список ссылок на функции в делегате пуст, то при попытке обращения к нему возникает исключительная ситуация System.NullReferenceException. Ошибки во время работы программы также возникнут если из списка удаляется (-, -=) не существующая в нем функция или удаление выпоняется по отношению к пустому списку.

Использование делегатов. Пример

private delegate void delegates(int n, ref double x);

public class MathOperations
{
            public static double Multiply(double value) {return value * 2; }
            public static double Square(double value) {return value * value; }
            public static double Quadre(double value) {return value * value * value; }
}

public static void Main()
{
            DoubleFun[] operations = new DoubleFun[3];  // 3 функциии
            operations[0] = new DoubleFun(MathOperations.Multiply);
            operations[1] = new DoubleFun(MathOperations.Square);
            operations[2] = new DoubleFun(MathOperations.Quadre);
            for (int i = 0; i < operations.Length; i++)
            {
                Console.WriteLine("Выпоняю операцию {0}:", i);
                Console.WriteLine(operations[i](2.0));
                Console.WriteLine(operations[i](7.94));
                Console.WriteLine(operations[i](1.414));
            }
}

В этом коде мы создаем массив делегатов DoubleFun, каждый элемент массива инициализируется ссылкой на отдельные операции, реализованные в классе MathOperations. Затем в цикле проходим по массив, применяя каждую операцию к трем отдельным значениям. Это один из способов применения делегатов: их можно группировать в массив, а несколько методов можно вызвать в цикле.