当前位置: 首页 > news >正文

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. 三者的比较与选择

特性DelegateEventUnityEvent
封装性低(外部可调用)高(外部只能订阅)
序列化不支持不支持支持
编辑器集成可视化配置
性能较低(有额外开销)
多播支持
使用场景纯代码回调代码事件系统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处理需要在编辑器中配置的交互

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

http://www.jsqmd.com/news/73865/

相关文章:

  • Wan2.2-T2V-A14B在综艺节目花絮自动生成中的尝试
  • 51单片机:了解最小核心系统
  • Flutter深度解析:从原理到实战的全栈开发指南
  • 2025年南通宠物医院权威推荐榜:专业诊疗与暖心服务口碑之选,附电话地址 - 品牌企业推荐师(官方)
  • 【专家私藏】量子算法调试秘技曝光:VSCode远程调试QPU模拟器全流程
  • MySQL基础篇——约束和事务
  • 2025 年 12 月彩钢瓦/厂房保养翻新厂家权威推荐榜:专业喷漆工艺与长效防护方案深度解析 - 品牌企业推荐师(官方)
  • 【VSCode量子编程环境搭建指南】:手把手教你5步配置Qiskit开发环境
  • 基于单片机大棚环境控制(温湿度,PH值)系统Proteus仿真(含全部资料)
  • 量子计算+机器学习调试实战(VSCode高阶技巧全公开)
  • 医疗健康AI Agent:开发难点与突破
  • Wan2.2-T2V-A14B支持自动字幕嵌入吗?多语种翻译生成测试
  • Java两种代理模式详解
  • 金融高频交易策略性能评估与优化框架
  • Wan2.2-T2V-A14B与Sora的技术路线差异比较
  • STM32 简单入门
  • ROG 魔盒透视版 AI 电竞路由器现已开售
  • 工程仿真许可优化:多软件协同作业冲突避免的动态分配方案
  • 【北理工-AAAI26】MODA:首个无人机多光谱目标检测数据集
  • Wan2.2-T2V-A14B与PixVerse、Kling等国产模型横向评测
  • 47、Linux技术知识综合解析
  • 48、Linux系统网络配置、故障排查与安全管理全解析
  • NVIDIA Profile Inspector完全攻略:释放显卡隐藏性能
  • DeepSeek-V3.1震撼发布:混合推理架构引领AI交互新纪元
  • 19、Git远程协作与推送操作全解析
  • Wan2.2-T2V-A14B能否生成符合ITU标准的国际电信联盟规范视频
  • AI重构营销:3K营销体系的技术实现路径与系统架构
  • 光颉科技TR50H系列TO-220封装50W功率电阻:高功率密度与便捷散热的解决方案
  • 【C++】--- 类型转换
  • 腾讯用“LLM+GNN“双剑合璧,广告推荐GMV暴涨2.8%!小白也能学会的冷启动解决方案