C# 委托/事件/UnityEvent 详解

1. 委托 (Delegate)

1.1 基本概念

委托是C#中的一种类型,它允许将方法作为参数传递,类似于C/C++中的函数指针,但类型安全。

1.2 委托声明与使用

基本语法:

csharp

复制代码
// 1. 声明委托类型
delegate void MyDelegate(string message);
delegate int CalculateDelegate(int a, int b);

// 2. 使用委托
public class DelegateExample
{
    // 定义与委托匹配的方法
    static void DisplayMessage(string msg)
    {
        Console.WriteLine($"Message: {msg}");
    }
    
    static int Add(int a, int b) => a + b;
    static int Multiply(int a, int b) => a * b;

    static void Main()
    {
        // 创建委托实例
        MyDelegate messageDelegate = new MyDelegate(DisplayMessage);
        // 调用委托
        messageDelegate("Hello Delegate!");
        
        // 多播委托示例
        CalculateDelegate calcDelegate = Add;
        calcDelegate += Multiply; // 添加另一个方法
        
        // 调用多播委托(会依次调用所有方法,但只返回最后一个结果)
        int result = calcDelegate(3, 4); // 返回Multiply的结果:12
    }
}
内置委托类型:

csharp

复制代码
// Action委托(无返回值)
Action<string> action1 = (msg) => Console.WriteLine(msg);
Action<int, int> action2 = (x, y) => Console.WriteLine(x + y);

// Func委托(有返回值)
Func<int, int, int> func1 = (a, b) => a + b;
Func<string, int> func2 = (s) => s.Length;

// Predicate委托(返回bool)
Predicate<int> isEven = (num) => num % 2 == 0;

2. 事件 (Event)

2.1 事件概念

事件是基于委托的特殊类型,提供了发布-订阅模式,增强封装性(外部只能订阅/取消订阅,不能触发)。

2.2 标准事件模式

csharp

复制代码
public class EventPublisher
{
    // 1. 定义事件委托(标准模式使用EventHandler<T>)
    public event EventHandler<MyEventArgs> MyEvent;
    
    // 2. 定义事件参数类
    public class MyEventArgs : EventArgs
    {
        public string Message { get; set; }
        public DateTime Time { get; set; }
    }
    
    // 3. 触发事件的方法
    protected virtual void OnMyEvent(string message)
    {
        MyEvent?.Invoke(this, new MyEventArgs 
        { 
            Message = message, 
            Time = DateTime.Now 
        });
    }
    
    public void DoSomething()
    {
        // 业务逻辑...
        OnMyEvent("Something happened!");
    }
}

public class EventSubscriber
{
    public void Subscribe(EventPublisher publisher)
    {
        // 订阅事件
        publisher.MyEvent += HandleEvent;
    }
    
    private void HandleEvent(object sender, EventPublisher.MyEventArgs e)
    {
        Console.WriteLine($"Event received: {e.Message} at {e.Time}");
    }
}

// 使用示例
var publisher = new EventPublisher();
var subscriber = new EventSubscriber();
subscriber.Subscribe(publisher);
publisher.DoSomething();

2.3 自定义委托事件

csharp

复制代码
public class TemperatureMonitor
{
    // 自定义委托
    public delegate void TemperatureChangedHandler(float oldTemp, float newTemp);
    
    // 基于自定义委托的事件
    public event TemperatureChangedHandler TemperatureChanged;
    
    private float _temperature;
    public float Temperature
    {
        get => _temperature;
        set
        {
            if (_temperature != value)
            {
                float oldTemp = _temperature;
                _temperature = value;
                TemperatureChanged?.Invoke(oldTemp, _temperature);
            }
        }
    }
}

3. UnityEvent

3.1 UnityEvent特点

UnityEvent是UnityEngine.Events命名空间下的类,专为Unity编辑器集成设计,支持序列化和可视化配置。

3.2 基本用法

csharp

复制代码
using UnityEngine;
using UnityEngine.Events;

public class UnityEventExample : MonoBehaviour
{
    // 1. 声明UnityEvent(无参数)
    [SerializeField]
    private UnityEvent onStartEvent;
    
    // 2. 声明带参数的UnityEvent
    [System.Serializable]
    public class StringEvent : UnityEvent<string> { }
    
    [SerializeField]
    private StringEvent onMessageEvent;
    
    // 3. 声明多个参数的UnityEvent
    [System.Serializable]
    public class DamageEvent : UnityEvent<GameObject, float, Vector3> { }
    
    [SerializeField]
    private DamageEvent onDamageTaken;
    
    void Start()
    {
        // 代码方式添加监听器
        onStartEvent.AddListener(OnStartHandler);
        onMessageEvent.AddListener(OnMessageHandler);
        
        // 触发事件
        onStartEvent.Invoke();
        onMessageEvent.Invoke("Hello from code!");
    }
    
    void OnStartHandler()
    {
        Debug.Log("Start event triggered!");
    }
    
    void OnMessageHandler(string message)
    {
        Debug.Log($"Message: {message}");
    }
    
    // 触发带多个参数的事件
    public void TakeDamage(float amount, Vector3 hitPoint)
    {
        onDamageTaken.Invoke(gameObject, amount, hitPoint);
    }
    
    void OnDestroy()
    {
        // 清理监听器(重要!避免内存泄漏)
        onStartEvent.RemoveAllListeners();
        onMessageEvent.RemoveAllListeners();
    }
}

3.3 编辑器中的可视化配置

在Unity Inspector中,UnityEvent会显示可配置的界面:

  1. 点击"+"添加事件条目

  2. 拖拽GameObject到Object字段

  3. 选择组件和方法

  4. 可以设置参数值

3.4 实际应用示例

csharp

复制代码
// 开关系统示例
public class Switch : MonoBehaviour
{
    public UnityEvent onTurnOn;
    public UnityEvent onTurnOff;
    
    private bool _isOn = false;
    
    public void Toggle()
    {
        _isOn = !_isOn;
        
        if (_isOn)
            onTurnOn.Invoke();
        else
            onTurnOff.Invoke();
    }
}

// 使用Switch的门
public class Door : MonoBehaviour
{
    public void Open()
    {
        // 开门动画/逻辑
        transform.position += Vector3.up * 2;
        Debug.Log("Door opened!");
    }
    
    public void Close()
    {
        // 关门动画/逻辑
        transform.position -= Vector3.up * 2;
        Debug.Log("Door closed!");
    }
}

// 在Unity编辑器中:
// 1. 将Switch脚本添加到开关对象
// 2. 将Door脚本添加到门对象
// 3. 在Switch的Inspector中:
//    - 拖拽门对象到onTurnOn事件
//    - 选择Door.Open方法
//    - 拖拽门对象到onTurnOff事件
//    - 选择Door.Close方法

4. 三者的比较与选择

特性 Delegate Event UnityEvent
封装性 低(外部可调用) 高(外部只能订阅)
序列化 不支持 不支持 支持
编辑器集成 可视化配置
性能 较低(有额外开销)
多播支持
使用场景 纯代码回调 代码事件系统 Unity编辑器交互

5. 最佳实践

5.1 内存管理

csharp

复制代码
public class EventManager : MonoBehaviour
{
    private Dictionary<string, UnityEvent> eventDictionary;
    
    void Start()
    {
        eventDictionary = new Dictionary<string, UnityEvent>();
    }
    
    public void StartListening(string eventName, UnityAction listener)
    {
        if (!eventDictionary.ContainsKey(eventName))
            eventDictionary[eventName] = new UnityEvent();
            
        eventDictionary[eventName].AddListener(listener);
    }
    
    public void StopListening(string eventName, UnityAction listener)
    {
        if (eventDictionary.ContainsKey(eventName))
            eventDictionary[eventName].RemoveListener(listener);
    }
    
    public void TriggerEvent(string eventName)
    {
        if (eventDictionary.ContainsKey(eventName))
            eventDictionary[eventName].Invoke();
    }
    
    void OnDestroy()
    {
        // 清理所有事件
        foreach (var unityEvent in eventDictionary.Values)
        {
            unityEvent.RemoveAllListeners();
        }
        eventDictionary.Clear();
    }
}

5.2 性能优化建议

  1. 避免频繁事件触发:特别是在Update方法中

  2. 使用对象池:对于频繁创建/销毁的事件参数

  3. 缓存委托实例:避免每次调用都创建新的委托

  4. 谨慎使用匿名方法:可能导致难以调试的内存泄漏

5.3 设计模式应用

csharp

复制代码
// 观察者模式实现
public interface IObserver
{
    void OnNotify(string eventType, object data);
}

public class Subject : MonoBehaviour
{
    private List<IObserver> observers = new List<IObserver>();
    
    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }
    
    public void UnregisterObserver(IObserver observer)
    {
        observers.Remove(observer);
    }
    
    protected void NotifyObservers(string eventType, object data = null)
    {
        foreach (var observer in observers)
        {
            observer.OnNotify(eventType, data);
        }
    }
}

总结

  1. 委托:是基础,用于方法引用和回调

  2. 事件:基于委托,提供更好的封装,适合组件间通信

  3. UnityEvent:Unity专用,提供编辑器集成,适合非程序员配置

在Unity开发中:

  • 使用C#事件处理纯代码逻辑

  • 使用UnityEvent处理需要在编辑器中配置的交互

  • 两者可以结合使用,发挥各自优势

相关推荐
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
神仙别闹1 天前
基于C#+MySQL实现(WinForm)个人聊天室软件
c#
伞伞悦读1 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
C语言小火车1 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁1 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
kybs19911 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
孙启超1 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust
此生决int1 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++
CoderYanger1 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
伞伞悦读1 天前
【第37期】Python JSON 与配置详解:序列化、反序列化、嵌套结构和配置文件
开发语言·python·json