委托与事件(Delegate 与 Event)
在C#中,函数本身并不是引用类型,也不是值类型,它只是一段可执行的代码,这也就意味着函数并不具备像整数、字符串等数据类型那样的值,本身也不是地址,因此无法直接作为参数传递。
委托在C#中所扮演的实际上是C++中“函数指针”这个角色,并且比它更为灵活。
- 委托可以将一个复杂的方法逻辑拆分为多个方法并形成链条,我们可以根据需要动态添加和移除链条上的任意一环。这个特性被称为“委托多播”功能。
- 委托可以让函数被作为参数传递。在执行某个函数或者方法时,动态地将另一个函数或方法“注入”到其中,让这个“注入”的函数或方法在适当的时机执行,这种手法在保证异步顺序时非常有用(比如响应网络请求的结果),同时也可以让具体业务和主干框架解耦。
public delegate void ShootDelegate(); public ShootDelegate onShoot; private void Start() { // 绑定发射子弹和产生枪口火焰的方法 onShoot += ShootBullet; onShoot += CreateMuzzleFlash; } private void Update() { if (Input.GetMouseButtonDown(0)) { // 开枪时调用委托,触发绑定的方法 if (onShoot != null) { onShoot(); } } }
上面代码定义了一个开枪的委托类型ShootDelegate,然后创建一个对应的委托实例onShoot,之后给委托绑定两个函数,分别是发射子弹和产生枪口火焰。在我们按下鼠标左键的时候,会去调用这个onShoot委托,之后函数ShootBullet和CreateMuzzleFlash就会被调用。给委托绑定函数时使用的是 += 这个操作,那自然也就有 -= 操作,可以把不需要执行的函数任务随时从委托清单上移除,这样下一次再调用onShoot时,这个被移除的函数就不会被调用。
关于 Invoke() 方法
//使用Invoke方法 delegate_SomeThing.Invoke(); //不使用Invoke方法 delegate_SomeThing();
这两种调用方式在功能上是等价的,但是在调用Invoke 时,Unity底层是将该方法添加到一个队列中,并在下一个更新循环中执行它有时候并不那么及时。
然而,使用Invoke方法的一大好处是,你可以使用安全访问符?检查委托是否为 null,以防止在没有任何方法订阅到委托时出现空引用异常。
关于 Action 和 Func
Action和Func是 C# 中的两个泛型委托类型,它们提供了一种方便的方式来定义和使用委托,尤其是在需要传递函数作为参数或返回函数的情况下。Action是一个不返回值的泛型委托类型,可以用于封装不带参数或带有参数的方法。Func是一个带有返回值的泛型委托类型,可以用于封装带有返回值的方法。
关于Delegate 和 Event
类或对象可以通过事件向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为"发布者",接收(或处理)事件的类称为“订阅者"。事件必须用委托类型来声明,并且只能由该委托类型来订阅。在Unity中封装了UnityEvent供我们使用。
public class Test : MonoBehaviour { // 定义一个 Unity Event public UnityEvent onClickEvent; private void Start() { // 获取按钮组件 Button button = GetComponent<Button>(); // 绑定点击事件 button.onClick.AddListener(OnButtonClick); } private void OnButtonClick() { // 触发 Unity Event,执行绑定的方法 onClickEvent.Invoke(); } }
以上是一个非常基本的按钮点击事件的实现,同时它还可以在Inspector面板上手动拖入一些函数进行可视化编辑。