В чём разница между Event и Delegate?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между 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 делегатом, поддерживающим множество подписчиков.
Основные различия в таблице
| Критерий | Delegate | Event |
|---|---|---|
| Основная роль | Указатель на метод / список методов. Механизм обратного вызова. | Контролируемый механизм публикации-подписки (паттерн 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, так как они обеспечивают лучшую архитектуру и защищают от случайных ошибок. Делегаты же используются внутри реализации событий или для специальных случаев, где нужна прямая передача метода как аргумента.