Какие виды делегатов существуют в C#?

Ответ

В C# делегаты — это типобезопасные указатели на методы. Основные виды:

  1. Пользовательские делегаты (обычные) — объявляются с ключевым словом delegate. Используются реже, так как в большинстве случаев их заменяют Action и Func.

    delegate int StringToIntConverter(string input);
  2. Action — предопределённый обобщённый делегат для методов, которые не возвращают значение (возвращают void). Может принимать от 0 до 16 параметров.

    Action<string> logAction = (message) => Console.WriteLine($"[LOG]: {message}");
    Action<int, int> sumAction = (a, b) => Console.WriteLine(a + b);
  3. Func — предопределённый обобщённый делегат для методов, которые возвращают значение. Последний параметр типа всегда указывает на возвращаемый тип.

    Func<int, int, int> addFunc = (a, b) => a + b;
    Func<string, bool> isNotEmptyFunc = (str) => !string.IsNullOrEmpty(str);
  4. Predicate<T> — специализированный делегат для проверки условия. Возвращает bool. По сути, это аналог Func<T, bool>, но с более ясным намерением в коде.

    Predicate<int> isEvenPredicate = (number) => number % 2 == 0;
    // Эквивалентно: Func<int, bool> isEvenFunc = ...
  5. EventHandler и EventHandler<TEventArgs> — стандартные делегаты для реализации событийной модели.

    public event EventHandler SomethingHappened;
    public event EventHandler<FileLoadedEventArgs> FileLoaded;

Почему это важно: Action и Func устраняют необходимость вручную объявлять множество делегатов, делая код чище и более унифицированным. Все делегаты в C# являются мультикаст-делегатами — они могут содержать ссылки на несколько методов и вызывать их по цепочке.

Action multiAction = () => Console.Write("Hello, ");
multiAction += () => Console.WriteLine("World!");
multiAction(); // Выведет: "Hello, World!"

Ответ 18+ 🔞

А, делегаты в C#! Ну это ж классика, блядь. Сидишь такой, пишешь код, и тут тебе надо метод в метод передать, как переменную. Ну типобезопасные указатели на методы, если по-умному. Смотри, как оно устроено, а то многие нихуя не понимают.

Во-первых, есть пользовательские делегаты. Объявляешь через delegate, как будто сигнатуру метода описываешь. Раньше это было модно, а сейчас нихуя не нужно, потому что придумали Action и Func. Но для истории, выглядит так:

delegate int StringToIntConverter(string input);

Ну то есть, объявил тип — теперь можешь переменную такого типа завести и в неё любой метод запихнуть, который строку принимает и инт возвращает. Старорежимно, но работает.

А теперь смотри, главные героиAction и Func. Это предопределённые делегаты, их дохуя видов, и они тебя спасут от писанины кучи своих делегатов.

Action — это для методов, которые нихуя не возвращают, то есть void. Может от нуля до 16 параметров таскать. Примеры, чтобы понятно было:

Action<string> logAction = (message) => Console.WriteLine($"[LOG]: {message}");
Action<int, int> sumAction = (a, b) => Console.WriteLine(a + b);

Первый — принимает строку и пишет в консоль. Второй — два инта принимает, сумму выводит. Возвращает void, поэтому Action. Всё просто, ёпта.

Func — а вот это уже для методов, которые что-то возвращают. Последний параметр типа — это всегда возвращаемый тип, запомни это, а то запутаешься. Пример:

Func<int, int, int> addFunc = (a, b) => a + b;
Func<string, bool> isNotEmptyFunc = (str) => !string.IsNullOrEmpty(str);

Видишь? Func<int, int, int> — первые два инта это параметры, последний — что возвращает. Строку принял — bool вернул. Красота, блядь.

Ещё есть Predicate<T> — это такой специальный олдскульный делегат для проверки условий. Возвращает bool, по сути то же самое, что Func<T, bool>, но название намекает, что тут будет какое-то условие. Чисто для читаемости, понимаешь?

Predicate<int> isEvenPredicate = (number) => number % 2 == 0;
// То же самое, что: Func<int, bool> isEvenFunc = ...

Ну и куда же без событий! Для них есть EventHandler и EventHandler<TEventArgs>. Это стандартные делегаты, на них завязана вся событийная модель в C#. Без них никуда, если пишешь что-то с событиями.

public event EventHandler SomethingHappened;
public event EventHandler<FileLoadedEventArgs> FileLoaded;

И главная фишка, которую многие забывают: все делегаты в C# — мультикаст-делегаты. Это значит, что в одну переменную можно навешать кучу методов, и они вызовутся по цепочке. Смотри, как прикольно:

Action multiAction = () => Console.Write("Hello, ");
multiAction += () => Console.WriteLine("World!");
multiAction(); // Выведет: "Hello, World!"

Два метода в одну кучу, и оба сработали. Вот так вот, блядь. Action и Func — это пиздец как удобно, потому что не надо каждый раз свой делегат объявлять. Бери и пользуйся, как конструктор. Главное — не перепутай, где void, а где нет, а то получишь ошибку, и будешь сидеть, чесать репу.