← Назад к вопросам

Что такое делегаты и события в C#? Чем делегат отличается от Func и Action?

2.0 Middle🔥 201 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое делегаты и события в C#?

Делегаты: типобезопасные указатели на методы

Делегат в C# — это тип, представляющий ссылки на методы с определённой сигнатурой и возвращаемым типом. Это аналог указателей на функции в C++, но полностью типобезопасный и объектно-ориентированный.

Ключевые особенности делегатов:

  • Безопасность типов — компилятор проверяет соответствие сигнатур
  • Поддержка multicast — один делегат может ссылаться на несколько методов
  • Интеграция с .NET событиями — основа механизма событий
// Объявление делегата
public delegate int MathOperation(int x, int y);

// Использование делегата
class Calculator
{
    public static int Add(int a, int b) => a + b;
    public static int Multiply(int a, int b) => a * b;
}

// Создание экземпляра делегата
MathOperation operation = Calculator.Add;
int result = operation(5, 3); // Результат: 8

События: реализация паттерна Наблюдатель

Событие — это специальный вид делегата, реализующий паттерн "Издатель-Подписчик" (Publisher-Subscriber). События обеспечивают безопасный механизм уведомления заинтересованных объектов о произошедших изменениях.

Особенности событий:

  • Инкапсуляция — внешний код может только подписаться/отписаться
  • Потокобезопасность — встроенная поддержка многопоточности
  • Стандартная сигнатураobject sender, EventArgs e
public class TemperatureSensor
{
    // Объявление события
    public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
    
    private double _currentTemp;
    
    public double CurrentTemperature
    {
        get => _currentTemp;
        set
        {
            if (_currentTemp != value)
            {
                _currentTemp = value;
                // Вызов события
                OnTemperatureChanged(new TemperatureChangedEventArgs(value));
            }
        }
    }
    
    protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e)
    {
        TemperatureChanged?.Invoke(this, e);
    }
}

// Подписка на событие
sensor.TemperatureChanged += (sender, e) => 
{
    Console.WriteLine($"Температура изменилась: {e.NewTemperature}°C");
};

Различия между делегатами, Func и Action

Обобщённые делегаты: Func и Action

Func и Action — это предопределённые обобщённые делегаты из пространства имен System, которые устраняют необходимость в объявлении собственных типов делегатов.

// Action - делегат без возвращаемого значения (void)
Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Hello, World!");

// Func - делегат с возвращаемым значением
Func<int, int, int> add = (x, y) => x + y;
int sum = add(5, 3); // 8

// Func с последним параметром как возвращаемый тип
Func<int, int, string> formatSum = (a, b) => $"Сумма: {a + b}";

Основные различия

1. Целевое назначение

  • Пользовательские делегаты — для конкретных доменных операций, требующих семантического именования
  • Func/Action — для общих операций, лямбда-выражений, LINQ

2. Гибкость и повторное использование

// Пользовательский делегат - более выразительный
public delegate bool ValidationRule(string input);

// Func - более универсальный, но менее выразительный
Func<string, bool> validationRule;

3. Читаемость кода

// С пользовательским делегатом яснее намерение
public delegate void LogMessage(string message, LogLevel level);
LogMessage logger = WriteToLog;

// С Action менее очевидна семантика
Action<string, LogLevel> loggerAction = WriteToLog;

4. Совместимость с событиями

  • События требуют конкретного типа делегата или EventHandler<T>
  • Func/Action не могут использоваться напрямую в объявлении событий

Практические рекомендации по выбору

Используйте пользовательские делегаты, когда:

  • Нужна ясная семантика и самодокументируемость кода
  • Делегат будет широко использоваться в кодовой базе
  • Требуется специфическая сигнатура с ref/out параметрами
  • Работаете с событиями (требуется конкретный тип делегата)

Используйте Func/Action, когда:

  • Нужен быстрый прототип или локальное использование
  • Работаете с LINQ и функциональными конструкциями
  • Сигнатура простая и универсальная
  • Хотите минимизировать объявления типов

Производительность и особенности реализации

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

// Эволюция от пользовательских делегатов к обобщённым
// Старый подход (до .NET 3.5)
public delegate TResult MyDelegate<T, TResult>(T arg);

// Современный подход
Func<T, TResult> myFunc;

Заключение

Делегаты представляют собой фундаментальный механизм C# для реализации обратных вызовов, событийной модели и функционального программирования. События — это специализированные делегаты, обеспечивающие безопасную реализацию паттерна Наблюдатель. Func и Action — это удобные обобщённые делегаты, которые сокращают количество объявляемых типов, но могут ухудшать читаемость кода при неправильном использовании.

Выбор между пользовательскими делегатами и Func/Action зависит от конкретного контекста: для публичных API и долгосрочных архитектурных решений предпочтительнее явные делегаты, для локальных и универсальных операций — Func/Action.

Что такое делегаты и события в C#? Чем делегат отличается от Func и Action? | PrepBro