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

Что такое MulticastDelegate?

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

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

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

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

Что такое MulticastDelegate в C# и Unity?

В C# и, соответственно, в Unity, MulticastDelegate — это базовый класс для всех делегатов, которые могут иметь в своем списке вызовов (invocation list) несколько методов. По сути, это делегат, который может ссылаться не на один, а на несколько методов одновременно. Когда такой делегат вызывается, все методы в его списке выполняются последовательно, в порядке их добавления.

Основные характеристики MulticastDelegate

  • Наследование: Все создаваемые вами делегаты в C# неявно наследуются от класса System.MulticastDelegate, который, в свою очередь, наследуется от System.Delegate.
  • Групповая рассылка (Multicast): Ключевая особенность — возможность "смешивать" (combine) несколько делегатов в один с помощью операторов + или +=, а также удалять методы с помощью - или -=.
  • Список вызовов (Invocation List): Внутри MulticastDelegate поддерживает список методов (Delegate[]), которые должны быть выполнены при его вызове.
  • Возвращаемые значения: Если multicast-делегат имеет возвращаемый тип (не void), то при вызове возвращается значение только последнего выполненного метода в цепочке. Это критически важно учитывать в архитектуре. Поэтому multicast-делегаты чаще всего используются с возвращаемым типом void.

Практическое использование в Unity

В Unity механизм событий (Events) и система сообщений (Messaging System) в значительной степени построены на концепции multicast-делегатов. Самый наглядный пример — UnityEvent.

using UnityEngine;
using UnityEngine.Events;

public class EventExample : MonoBehaviour
{
    // Объявляем UnityEvent - специализированную реализацию multicast-делегата
    // с возможностью настройки через Inspector.
    public UnityEvent OnCustomEvent;

    void Start()
    {
        // Добавляем (подписываем) несколько методов на событие
        OnCustomEvent.AddListener(MethodOne);
        OnCustomEvent.AddListener(MethodTwo);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // Вызов события. Будут выполнены MethodOne и MethodTwo.
            OnCustomEvent.Invoke();
        }
    }

    void MethodOne()
    {
        Debug.Log("Метод 1 выполнен.");
    }

    void MethodTwo()
    {
        Debug.Log("Метод 2 выполнен.");
    }

    void OnDestroy()
    {
        // Важно: отписываем методы при уничтожении объекта во избежание ошибок ссылок.
        OnCustomEvent.RemoveListener(MethodOne);
        OnCustomEvent.RemoveListener(MethodTwo);
    }
}

Создание и работа с пользовательским Multicast-Делегатом

// 1. Объявляем делегат. Он автоматически становится MulticastDelegate.
public delegate void GameActionDelegate(string message);

public class Player
{
    // 2. Создаем экземпляр делегата как поле класса (аналог события).
    public GameActionDelegate OnActionPerformed;

    public void PerformAction(string actionName)
    {
        Debug.Log($"Игрок выполнил: {actionName}");

        // 3. Безопасный вызов. Проверка на null обязательна,
        // так как если на делегат нет подписчиков, он будет равен null.
        OnActionPerformed?.Invoke(actionName);
    }
}

public class GameLogger
{
    public void LogToConsole(string msg) => Debug.Log($"Лог: {msg}");
}

public class AchievementSystem
{
    public void CheckAchievement(string msg) => Debug.Log($"Проверка достижения для: {msg}");
}

// Где-то в коде (например, в GameManager):
Player player = new Player();
GameLogger logger = new GameLogger();
AchievementSystem achievements = new AchievementSystem();

// 4. Подписываем несколько методов (Multicast).
player.OnActionPerformed += logger.LogToConsole;
player.OnActionPerformed += achievements.CheckAchievement;

// При вызове `player.PerformAction("Прыжок")` оба метода выполнятся.
// Вывод:
// "Игрок выполнил: Прыжок"
// "Лог: Прыжок"
// "Проверка достижения для: Прыжок"

// 5. Отписка одного из методов.
player.OnActionPerformed -= achievements.CheckAchievement;

Ключевые преимущества и особенности в контексте Unity-разработки

  • Инверсия зависимостей и слабая связанность: Компоненты могут сообщать о своих действиях, не зная, какие другие системы (логирование, UI, достижения, звук) на них подписаны. Это краеугольный камень чистой и масштабируемой архитектуры в Unity.
  • Визуальное программирование: UnityEvent позволяет связывать вызовы методов между объектами прямо в Inspector, что мощно используется для внутрисценовой логики, анимационных событий и UI.
  • Управление памятью и ошибки: Самый частый источник ошибок — неотписка от событий. Если объект, метод которого подписан на делегат, уничтожается, а отписка не происходит, делегат сохраняет ссылку на "мертвый" метод. При следующем вызове это приведет к MissingReferenceException. Паттерн "подписка в OnEnable, отписка в OnDisable" — стандарт де-факто.
  • Порядок выполнения: Разработчик должен помнить, что порядок вызова методов не гарантирован (особенно при динамической подписке/отписке), если это критично для логики. Для строгого порядка нужна более сложная система (например, очередь приоритетов).

Итог: MulticastDelegate — это не просто техническая деталь языка C#, а фундаментальный механизм, лежащий в основе событийной модели (Event-driven) Unity. Понимание его работы, преимуществ (гибкость, слабая связанность) и подводных камней (управление жизненным циклом подписки, возвращаемые значения) абсолютно необходимо для создания сложных, надежных и поддерживаемых игр.

Что такое MulticastDelegate? | PrepBro