Встречается на 44% собеседований по C#

Какие знаешь делегаты

Делегаты в C# — это типобезопасные ссылки на методы, которые позволяют передавать методы как параметры, хранить их в переменных и вызывать динамически. Они лежат в основе событий, обратных вызовов и многих возможностей LINQ. В C# есть встроенные делегаты Action, Func и Predicate, а также возможность объявлять собственные типы делегатов с помощью ключевого слова delegate.

Встроенные делегаты

Вместо объявления собственного делегата для большинства сценариев можно использовать готовые обобщённые типы:

  • Action — делегат для метода, который не возвращает значение (void). Может принимать от 0 до 16 параметров. Например, Action<string> — метод с одним строковым параметром.
csharp
Action<string> printAction = s => Console.WriteLine(s);
printAction("Hello, Action!");
  • Func — делегат для метода, который возвращает значение. Последний параметр в угловых скобках — тип возвращаемого значения, перед ним — типы параметров (от 0 до 16). Например, Func<int, int, int> — метод с двумя параметрами int и возвращаемым int.
csharp
Func<int, int, int> sumFunc = (x, y) => x + y;
int result = sumFunc(5, 3);  // result = 8
  • Predicate<T> — специализация Func<T, bool>: принимает один параметр и всегда возвращает bool. Используется для проверки условий, например, в методах поиска коллекций.
csharp
Predicate<int> isPositive = x => x > 0;
bool positive = isPositive(-1);  // positive = false

Создание собственных делегатов

Если встроенных делегатов недостаточно (например, нужен ref или out параметр), можно объявить свой тип через delegate. Синтаксис похож на объявление метода, но без тела:

csharp
public delegate void MyCustomDelegate(string message);

public class Program
{
    public static void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }

    static void Main()
    {
        MyCustomDelegate del = ShowMessage;
        del("Hello, Custom Delegate!");
    }
}

Экземпляр делегата можно создать через конструктор new MyCustomDelegate(ShowMessage) или просто присвоить метод (как в примере). Делегаты поддерживают групповое преобразование методов, а также комбинирование через + и - (мультикаст).

Применение делегатов

Делегаты используются в трёх ключевых областях:

  • Обратные вызовы: метод передаётся в другой метод для вызова в определённый момент. Например, в асинхронном программировании или при обработке событий.
  • События: события в C# основаны на делегатах. Тип события — это делегат, определяющий сигнатуру обработчиков.
  • LINQ: многие методы расширения, такие как Where, Select, Aggregate, принимают делегаты (Func или Predicate) для задания логики.

Делегаты обеспечивают слабую связанность: вызывающий код не знает, какая именно реализация будет выполнена, что упрощает расширение и тестирование.

Подводные камни

  • Мультикаст-делегаты: если делегат указывает на несколько методов, возвращаемое значение будет от последнего вызванного метода. Для Action это не проблема, но для Func стоит учитывать.
  • Исключения: при мультикасте, если один метод выбрасывает исключение, последующие методы не вызовутся.
  • Null-проверка: вызов делегата без проверки на null приведёт к NullReferenceException. Используйте ?.Invoke(...) или проверку if (del != null).
  • Захват переменных в лямбда-выражениях: лямбды, присвоенные делегатам, захватывают переменные из внешнего контекста, что может привести к неожиданному поведению при изменении переменной после создания делегата.

Коротко

  • Делегат — это ссылка на метод; встроенные типы: Action (void), Func (возвращает значение), Predicate (возвращает bool).
  • Собственный делегат объявляется через delegate, поддерживает мультикаст и групповое преобразование.
  • Делегаты применяются для обратных вызовов, событий и в LINQ.
  • Помните о проверке на null и особенностях мультикаста.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы