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

В чём разница между Event и Delegate?

2.0 Middle🔥 163 комментариев
#C# и ООП#Паттерны проектирования

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

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

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

Разница между Event и Delegate в Unity и C#

Delegate и Event — фундаментальные концепции языка C#, используемые в Unity для реализации событийной модели и обратных вызовов. Их часто путают, но они играют разные, хотя и взаимосвязанные, роли в архитектуре приложения.

Определение и роль Delegate

Delegate — это типобезопасный указатель на метод. Он определяет сигнатуру (тип возвращаемого значения и параметры), которую могут иметь методы, которые он будет ссылаться. Delegate является основой для механизма обратных вызовов.

// Определение делегата
public delegate void DamageCallback(float damageAmount);

// Метод, соответствующий сигнатуре делегата
void LogDamage(float damage) {
    Debug.Log($"Получен урон: {damage}");
}

// Использование делегата как переменной
DamageCallback callback = LogDamage;
callback(10.5f); // Вызов метода через делегат

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

  • Это тип, который можно объявлять, инстанцировать и вызывать.
  • Делегатная переменная может хранить ссылку на один метод (в случае обычного делегата) или на список методов (в случае multicast делегата).
  • Он может быть объявлен в любом месте класса и публично присваиваться извне, что даёт полный контроль над ссылкой.

Определение и роль Event

Event — это специальный паттерн на основе делегата, предоставляющий контролируемый, безопасный и интуитивно понятный механизм для публикации и подписки на события. С технической стороны, событие — это ограниченный (обёрнутый) делегат, объявленный с ключевым словом event.

public class PlayerHealth {
    // 1. Определяем делегат-сигнатуру события
    public delegate void OnHealthChanged(float newHealth);
    
    // 2. Объявляем событие на основе этого делегата
    public event OnHealthChanged HealthChanged;
    
    public void TakeDamage(float damage) {
        // ...
        // 3. Вызов (публикация) события
        HealthChanged?.Invoke(currentHealth);
    }
}

// В другом классе подписываемся на событие
playerHealth.HealthChanged += UpdateHealthUI;

Ключевые особенности события (event):

  • Это не новый тип, а модификатор для делегатной переменной, добавляющий ограничения.
  • Вне класса, где объявлено событие, можно выполнять только две операции: подписаться (+=) и отписаться (-=). Невозможно присвоить событию новое значение (=) или напрямую вызвать его (Invoke()). Это защищает инкапсуляцию.
  • Вызов (Invoke) события возможен только внутри класса-владельца. Это делает событие "паблишером", который контролирует момент публикации.
  • Событие по умолчанию является multicast делегатом, поддерживающим множество подписчиков.

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

КритерийDelegateEvent
Основная рольУказатель на метод / список методов. Механизм обратного вызова.Контролируемый механизм публикации-подписки (паттерн Observer).
ИнкапсуляцияНизкая. Публичная делегатная переменная может быть полностью переприсвоена или вызвана из любого внешнего кода.Высокая. Извне доступны только += и -=.
Инициатор вызоваЛюбой код, имеющий доступ к делегатной переменной.Только класс, в котором объявлено событие.
Архитектурный паттернИнструмент для реализации.Реализация паттерна "Событие" сам по себе.
Частое использование в UnityДля передачи методов в параметры (например, в List.Sort с компаратором), создания кастомных callback-систем.Для всех компонентных событий (OnCollisionEnter, Button.onClick), коммуникации между скриптами (например, GameManager.OnGameStarted).

Практический пример в Unity

В Unity события используются повсеместно для межобъектного взаимодействия без жестких связей.

// GameManager как источник события
public class GameManager : MonoBehaviour {
    public delegate void GameEvent();
    public event GameEvent OnLevelCompleted;
    
    void CompleteLevel() {
        // Внутренний код завершения уровня...
        OnLevelCompleted?.Invoke(); // Публикация события
    }
}

// UIManager и AudioManager как независимые подписчики
public class UIManager : MonoBehaviour {
    void Start() {
        FindObjectOfType<GameManager>().OnLevelCompleted += ShowLevelCompleteScreen;
    }
    void ShowLevelCompleteScreen() {
        // Обновляет UI
    }
}

public class AudioManager : MonoBehaviour {
    void Start() {
        FindObjectOfType<GameManager>().OnLevelCompleted += PlayVictorySound;
    }
    void PlayVictorySound() {
        // Проигрывает звук
    }
}

Итог: Delegate — это низкоуровневый тип для ссылки на методы, а Event — это высокоуровневый, безопасный паттерн на основе делегата, созданный специально для реализации системы событий, где источник контролирует публикацию, а подписчики могут только добавлять или удалять свои реакции. В Unity для коммуникации между системами почти всегда следует использовать events, так как они обеспечивают лучшую архитектуру и защищают от случайных ошибок. Делегаты же используются внутри реализации событий или для специальных случаев, где нужна прямая передача метода как аргумента.