Каким типом данных является делегат в C#?

«Каким типом данных является делегат в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Делегат в C# — это безопасный тип указателя на метод, который определяет сигнатуру (возвращаемый тип и параметры). С точки зрения системы типов, делегат является ссылочным типом (class), производным от System.MulticastDelegate.

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

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

// 1. Объявление типа делегата
public delegate int Processor(int x, int y);

// 2. Методы, совместимые с сигнатурой делегата
static int Add(int a, int b) => a + b;
static int Multiply(int a, int b) => a * b;

// 3. Создание экземпляра делегата и вызов
Processor operation = Add;
int result = operation(5, 3); // result = 8

// 4. Multicast: делегат может ссылаться на несколько методов
operation += Multiply;
result = operation(5, 3); // Последовательно вызовет Add(5,3), затем Multiply(5,3).
                           // В result будет записан результат последнего метода (15).

Почему делегаты важны? Они — основа событийной модели .NET и LINQ.

Современные встроенные обобщённые делегаты (чаще используются на практике):

  • Action — для методов, которые не возвращают значение (void).
    Action<string> logger = message => Console.WriteLine($"[LOG]: {message}");
    logger("App started");
  • Func — для методов, возвращающих значение.
    Func<int, int, int> calculator = (x, y) => x + y;
    int sum = calculator(10, 20); // sum = 30
  • Predicate<T> — частный случай Func<T, bool>, используется для проверки условий.

Ключевой паттерн на делегатах — Callback:

public void ProcessData(List<int> data, Action<int> progressCallback)
{
    for (int i = 0; i < data.Count; i++)
    {
        // ... обработка ...
        progressCallback?.Invoke(i + 1); // Вызов callback для обновления UI
    }
}

// Использование
ProcessData(myList, progress => Console.WriteLine($"Обработано: {progress} элементов"));