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

Какие знаешь виды делегатов?

1.3 Junior🔥 121 комментариев
#C# и ООП

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Виды делегатов в C#

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

Основные концепции

1. Простые делегаты (Delegate)

Пользовательский делегат определяет сигнатуру метода:

// Определение делегата
public delegate void OnPlayerDeath(Player player);
public delegate int MathOperation(int a, int b);
public delegate string TextProcessor(string text);

public class GameManager : MonoBehaviour
{
    // Экземпляр делегата
    private OnPlayerDeath deathCallback;
    
    private void Start()
    {
        // Присвоение метода делегату
        deathCallback = PlayerDeadHandler;
        deathCallback += LogDeath;  // Добавление ещё одного метода
        
        // Вызов делегата (вызывает все добавленные методы)
        Player player = new Player();
        deathCallback?.Invoke(player);
    }
    
    private void PlayerDeadHandler(Player player)
    {
        Debug.Log($"{player.name} is dead!");
    }
    
    private void LogDeath(Player player)
    {
        Debug.Log($"Logging death of {player.name}");
    }
}

2. Action (встроенный делегат)

Action — это предопределённый делегат для методов, которые ничего не возвращают:

public class InputHandler : MonoBehaviour
{
    // Action без параметров
    public Action OnJumpPressed;
    
    // Action с параметрами
    public Action<string> OnPlayerDamaged;
    public Action<int, Vector3> OnDamage;
    
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            OnJumpPressed?.Invoke();  // Вызов
        }
        
        if (Input.GetKeyDown(KeyCode.E))
        {
            OnPlayerDamaged?.Invoke("Fire");
            OnDamage?.Invoke(10, transform.position);
        }
    }
}

// Использование
public class Player : MonoBehaviour
{
    private void Start()
    {
        InputHandler input = GetComponent<InputHandler>();
        
        input.OnJumpPressed += Jump;
        input.OnPlayerDamaged += TakeDamage;
        input.OnDamage += ApplyDamage;
    }
    
    private void Jump() => GetComponent<Rigidbody>().velocity += Vector3.up * 5f;
    private void TakeDamage(string damageType) => Debug.Log($"Took {damageType} damage");
    private void ApplyDamage(int amount, Vector3 position) => Debug.Log($"Damage: {amount} at {position}");
}

3. Func (делегат с возвращаемым значением)

Func используется для методов, которые возвращают значение:

public class Calculator : MonoBehaviour
{
    // Func<параметры..., возвращаемый тип>
    public Func<int, int, int> Operation;
    
    public void Initialize()
    {
        // Присвоение методов
        Operation = Add;
        int result1 = Operation(5, 3);  // 8
        
        Operation = Multiply;
        int result2 = Operation(5, 3);  // 15
        
        // Lambda выражение
        Operation = (a, b) => a * a + b * b;
        int result3 = Operation(3, 4);  // 25
    }
    
    private int Add(int a, int b) => a + b;
    private int Multiply(int a, int b) => a * b;
}

// Другие примеры Func
public class DataProcessor
{
    private Func<string, int> StringToLength;      // Один параметр
    private Func<Player, bool> IsPlayerAlive;      // Пользовательский тип
    private Func<Vector3, Vector3, float> Distance; // Два параметра
    
    private void Setup()
    {
        StringToLength = str => str.Length;
        IsPlayerAlive = player => player.Health > 0;
        Distance = (a, b) => Vector3.Distance(a, b);
    }
}

4. Предикат (Predicate)

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

public class EnemyManager : MonoBehaviour
{
    private List<Enemy> enemies = new List<Enemy>();
    
    public void Setup()
    {
        // Predicate для фильтрации
        Predicate<Enemy> isAlive = enemy => enemy.Health > 0;
        Predicate<Enemy> isNearPlayer = enemy => Vector3.Distance(enemy.transform.position, transform.position) < 10f;
        
        // Использование с List
        List<Enemy> aliveEnemies = enemies.FindAll(isAlive);
        List<Enemy> nearEnemies = enemies.FindAll(isNearPlayer);
        
        // Или прямо
        Enemy target = enemies.Find(e => e.Health > 0 && e.Level >= 5);
    }
}

5. События (Event)

Event — это обёртка над делегатом для контролируемой подписки:

public class Player : MonoBehaviour
{
    // Event с делегатом
    public delegate void OnHealthChanged(float newHealth);
    public event OnHealthChanged HealthChanged;
    
    private float health = 100f;
    
    public void TakeDamage(float damage)
    {
        health -= damage;
        HealthChanged?.Invoke(health);  // Вызов события
    }
}

// Подписка на событие
public class HealthBar : MonoBehaviour
{
    private void Start()
    {
        Player player = FindObjectOfType<Player>();
        player.HealthChanged += UpdateHealthUI;  // Подписка
    }
    
    private void UpdateHealthUI(float health)
    {
        Debug.Log($"Health: {health}");
    }
}

Сравнение делегатов

Делегат vs Action:

// Делегат (пользовательский)
public delegate void MyDelegate(int value);
private MyDelegate callback;  // Явное объявление

// Action (встроенный, проще)
private Action<int> callback;  // Быстрее писать

Action vs Func:

// Action — ничего не возвращает
Action<int> logValue = x => Debug.Log(x);

// Func — возвращает значение
Func<int, int> square = x => x * x;  // Возвращает int
int result = square(5);  // 25

Лямбда выражения с делегатами

public class LambdaExamples : MonoBehaviour
{
    private void Start()
    {
        // Без параметров
        Action greet = () => Debug.Log("Hello!");
        greet();  // Hello!
        
        // Один параметр
        Action<string> sayName = name => Debug.Log($"My name is {name}");
        sayName("Alice");  // My name is Alice
        
        // Несколько параметров
        Func<int, int, int> add = (a, b) => a + b;
        int sum = add(5, 3);  // 8
        
        // Многострочная лямбда
        Action<int> calculate = x =>
        {
            int square = x * x;
            int cube = x * x * x;
            Debug.Log($"Square: {square}, Cube: {cube}");
        };
        calculate(3);  // Square: 9, Cube: 27
    }
}

Практический пример: система команд

public class CommandSystem : MonoBehaviour
{
    // Хранилище команд
    private Dictionary<string, Action> commands = new Dictionary<string, Action>();
    private Dictionary<string, Func<string, string>> queries = new Dictionary<string, Func<string, string>>();
    
    public void RegisterCommand(string name, Action command)
    {
        commands[name] = command;
    }
    
    public void RegisterQuery(string name, Func<string, string> query)
    {
        queries[name] = query;
    }
    
    public void ExecuteCommand(string name)
    {
        if (commands.TryGetValue(name, out Action command))
        {
            command.Invoke();
        }
    }
    
    public string ExecuteQuery(string name, string param)
    {
        if (queries.TryGetValue(name, out Func<string, string> query))
        {
            return query.Invoke(param);
        }
        return null;
    }
}

Итоги

ТипНазначениеВозвратПример
DelegateПользовательский указательЛюбойdelegate void MyDel();
ActionМетод без возвратаvoidAction<int> log = x => Debug.Log(x);
FuncМетод с возвратомTFunc<int, int> square = x => x * x;
PredicateФильтрация (bool)boolPredicate<T> check = x => x > 0;
EventПодписка на событияЗависитevent Action OnEvent;

Делегаты — это основа для построения гибких и расширяемых систем в Unity!

Какие знаешь виды делегатов? | PrepBro