Что такое делегат в C# и зачем он нужен? Не понимаю синтаксис delegate
Читаю учебник по C#, дошёл до главы про делегаты (delegate) и совсем запутался. Везде пишут, что «делегат — это указатель на метод», но мне это ничего не объясняет. Зачем вообще передавать метод, если я могу просто вызвать его напрямую?
Вот пример, который меня смущает. Я объявил delegate, но не понимаю, что происходит при присваивании:
delegate int Operation(int a, int b);
class Program
{
static int Add(int x, int y) => x + y;
static void Main()
{
Operation op = Add; // вот эта строка непонятна
Console.WriteLine(op(3, 4));
}
}
Как op вдруг становится методом Add? И главное — зачем так делать, если можно написать просто Add(3, 4)?
2 ответа
Делегат — это тип, который описывает сигнатуру метода (какие параметры принимает и что возвращает). Переменная такого типа может хранить ссылку на любой метод с подходящей сигнатурой и вызывать его.
В твоём примере Operation — это тип «метод, принимающий два int и возвращающий int». Строка Operation op = Add; кладёт в переменную ссылку на метод Add. Теперь op(3, 4) фактически вызывает Add(3, 4).
Смысл не в том, чтобы заменить прямой вызов, а в том, чтобы передавать поведение как данные. Например, метод сортировки может принимать делегат-компаратор и не знать заранее, как именно сравнивать:
delegate int Operation(int a, int b);
static int Add(int a, int b) => a + b;
static int Mul(int a, int b) => a * b;
static void PrintResult(Operation f, int a, int b)
{
Console.WriteLine(f(a, b));
}
// один и тот же метод, разное поведение
PrintResult(Add, 3, 4); // 7
PrintResult(Mul, 3, 4); // 12
Именно на делегатах построены события, колбэки и весь LINQ. Так что это не «лишняя обёртка», а основа для гибкого кода.
Добавлю аналогию, которая мне в своё время помогла. Делегат — это как «контакт» в телефоне: ты не звонишь напрямую, а сохраняешь номер под именем и потом набираешь по имени. Можно поменять, кому привязан контакт, не меняя код, который «звонит».
Ещё важно: в один делегат можно подписать несколько методов (multicast), и при вызове отработают все по очереди:
Action hello = () => Console.WriteLine("Привет");
hello += () => Console.WriteLine("Пока");
hello(); // выведет обе строки
Это как раз то, на чём держатся события.