在Unity开发中,C#的委托(Delegate)和事件(Event)就像一对双胞胎兄弟,曾困惑于它们的本质区别。本文将通过实战案例揭开这对”孪生兄弟”的神秘面纱,了解它们的正确使用姿势。
一、委托:灵活的函数指针
1.1 委托的本质
委托本质上是一个类型安全的函数指针容器,可以存储多个方法引用。我们可以通过+=运算符实现多播委托:
public delegate void HealthHandler(float health); public class Player { public HealthHandler OnHealthChanged; public void TakeDamage(float damage) { currentHealth -= damage; OnHealthChanged?.Invoke(currentHealth); } }
1.2 委托的妙用场景
- 实现回调系统(如成就解锁)
- 跨脚本通信(非MonoBehaviour类间通信)
- 异步操作处理(如资源加载完成回调)
二、事件:安全的委托封装器
2.1 事件的核心特征
事件在委托基础上添加了访问控制层,使用event
关键字声明:
public class Achievements { public event Action OnAchievementUnlocked; private void Unlock(string achievementName) { OnAchievementUnlocked?.Invoke(achievementName); } }
2.2 事件的安全机制
- 封装性:外部类只能订阅/取消订阅(+=/-=)
- 访问控制:仅声明类可触发事件
- 线程安全:编译器自动生成线程同步代码
三、关键差异对比表
特性 | 委托 | 事件 |
---|---|---|
访问权限 | 公共可调用 | 仅声明类可触发 |
赋值操作 | 支持=赋值 | 仅支持+=/-= |
多播能力 | 原生支持 | 通过委托机制支持 |
空引用检查 | 需手动检查 | 自动生成空检查代码 |
设计目的 | 通用回调机制 | 观察者模式实现 |
典型应用场景 | 函数参数、回调列表 | UI交互、游戏事件通知 |
四、Unity实战案例解析
4.1 委托实现技能冷却系统
public class SkillSystem { public delegate void CooldownCallback(int skillID); public CooldownCallback OnCooldownStart; public void CastSkill(int skillID) { // 释放技能逻辑 OnCooldownStart?.Invoke(skillID); } } // 使用端 skillSystem.OnCooldownStart += id => Debug.Log($"技能{id}开始冷却");
4.2 事件实现成就系统
public class AchievementSystem { public event Action<string> OnAchievementCompleted; public void CompleteAchievement(string name) { OnAchievementCompleted?.Invoke(name); } } // 订阅端 achievementSystem.OnAchievementCompleted += name => { PlayEffect(name); UpdateUI(name); };
五、设计选择指南
✅ 使用委托当:
- 需要函数作为参数传递时
- 实现回调函数列表时
- 需要直接控制调用时
✅ 使用事件当:
- 构建发布-订阅系统时
- 需要保护触发权限时
- 处理UI交互事件时
- 实现系统解耦时
六、性能优化贴士
- 避免每帧触发高频事件(如Update中的物理检测)
- 及时取消订阅防止内存泄漏
- 对高频事件使用EventManager集中管理
- 值类型参数优先使用ref传递
- 使用对象池重用事件参数
结语
理解委托和事件的区别就像掌握魔法世界中的两种咒语:委托是灵活的基础咒语,而事件则是施加了保护咒的强化版本。在Unity开发中,建议80%的场景使用事件,仅在需要完全控制调用时使用原始委托。记住:事件不是委托的替代品,而是其安全封装形态。合理运用这对黄金组合,可以让游戏代码既优雅又高效!