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

Что делает Action в Делегате?

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

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

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

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

Ответ на вопрос: «Что делает Action в Делегате?»

В контексте C# и Unity, ключевое слово Action представляет собой специальный тип делегата (delegate), который является фундаментальной частью системы событий и асинхронных операций в языке. Понимание Action критически важно для разработчика Unity, так как оно широко используется для реализации callback-механизмов, событийной архитектуры и взаимодействия между компонентами игры.

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

Action — это предопределенный делегат без возвращаемого значения, входящий в пространство имен System. Он используется для инкапсуляции методов, которые выполняют некоторое действие, но не возвращают результат (void методы). Это делает его идеальным для ситуаций, где нужно просто «что-то сделать», например, вызвать функцию в ответ на событие.

Основная роль Action в делегате — это предоставление удобного, типобезопасного (type-safe) способа работы с методами как с объектами. В отличие от создания собственных делегатных типов для каждого случая, Action стандартизирует подход для методов без возвращаемого значения.

Сравнение с другими типами делегатов

В семействе предопределенных делегатов в C# также существуют:

  • Func<T> — делегат, который возвращает значение указанного типа T.
  • Predicate<T> — специальный делегат для методов, возвращающих bool (часто используется в фильтрации).

Action является их «безрезультативным» братом. Он может иметь от 0 до 16 входных параметров (через generic типы Action<T1, T2, ..., T16>).

Примеры использования в C# и Unity

1. Базовый пример: Action без параметров

Это часто используется для простых callback-ов, например, по завершении анимации или загрузки.

using System;

public class EventManager
{
    // Объявляем поле типа Action
    public Action OnGameStarted;

    public void StartGame()
    {
        // Выполняем логику запуска...
        Debug.Log("Game Logic Started");

        // Проверяем, подписан ли кто-то на событие, и вызываем его
        if (OnGameStarted != null)
        {
            OnGameStarted.Invoke(); // или просто OnGameStarted();
        }
    }
}

// Использование в другом классе
public class PlayerController
{
    public void SubscribeToEvents(EventManager manager)
    {
        // Подписываем метод PlayStartSound на событие OnGameStarted
        manager.OnGameStarted += PlayStartSound;
    }

    private void PlayStartSound()
    {
        Debug.Log("Playing start sound!");
    }
}

2. Action с параметрами (Action<T>)

В Unity это чрезвычайно полезно для передачи данных вместе с событием, например, передача количества здоровья при получении урона.

using System;

public class HealthSystem
{
    // Делегат, принимающий int параметр (новое значение здоровья)
    public Action<int> OnHealthChanged;

    private int currentHealth = 100;

    public void TakeDamage(int damage)
    {
        currentHealth -= damage;
        // Вызываем событие, передавая новое значение здоровья
        OnHealthChanged?.Invoke(currentHealth); // Используем оператор ?. для безопасного вызова
    }
}

// UI компонент, который подписывается на это событие
public class HealthUI : MonoBehaviour
{
    [SerializeField] private Text healthText;
    [SerializeField] private HealthSystem healthSystem;

    void Start()
    {
        // Подписываем метод UpdateHealthDisplay, который принимает int
        healthSystem.OnHealthChanged += UpdateHealthDisplay;
    }

    private void UpdateHealthDisplay(int newHealth)
    {
        healthText.text = $"Health: {newHealth}";
    }
}

3. Использование в асинхронных операциях и колбэках Unity

Многие API Unity, особенно новые асинхронные методы (например, UnityWebRequest), или системы вроде Addressables, активно используют Action или UnityAction (их Unity-специфичную версию) для callback-ов.

using UnityEngine;
using System.Collections;

public class AsyncLoader : MonoBehaviour
{
    // Используем Action для сигнала о завершении
    public Action OnLoadComplete;

    void Start()
    {
        StartCoroutine(LoadDataCoroutine());
    }

    IEnumerator LoadDataCoroutine()
    {
        yield return new WaitForSeconds(2.0f); // Имитация загрузки
        Debug.Log("Data Loaded");

        // Информируем все подписчиков о завершении
        OnLoadComplete?.Invoke();
    }
}

Преимущества использования Action

  • Упрощение кода: Не нужно каждый раз объявлять новый делегатный тип (delegate void MyDelegate()).
  • Гибкость: Поддержка до 16 параметров через generics покрывает большинство практических случаев.
  • Интеграция с C# и Unity: Полная совместимость с языковыми features, такими как лямбда-выражения и анонимные методы, что делает код очень компактным.
    // Пример с лямбдой
    healthSystem.OnHealthChanged += (health) => { Debug.Log($"Health changed to: {health}"); };
    
  • Безопасность: Типобезопасность предотвращает ошибки, связанные с передачей методов неправильного типа.

Заключение

В итоге, Action в делегате служит мощным и стандартизированным механизмом для создания событийных систем (event systems), реализации обратных вызовов (callbacks) и организации коммуникации между несвязанными компонентами игры в Unity. Его понимание и правильное применение напрямую влияет на качество архитектуры проекта, делая код более читаемым, модульным и легко расширяемым. Для Unity Developer мастерство в работе с Action, Func и другими делегатами является одним из ключевых навыков для построения сложной и эффективной игровой логики.

Что делает Action в Делегате? | PrepBro