概念
在 C#
中,事件event
用于实现基于委托的回调机制。
事件允许对象通知其他对象特定的操作已经发生,这些对象可以选择性地对这些操作进行响应。
事件通常用于实现一种发布-订阅模型,其中一个对象(发布者)发出事件,而其他对象(订阅者)侦听事件并在事件发生时执行其操作。
应用场景:
我们假设一个场景,现在如下图有一名角色升级,而升级需要足够exp
:
- 如果
获得的exp
<升级所需的exp
,发布者表示经验还不够,那么订阅者不予理会; - 如果
获得的exp
≥ \ge ≥升级所需的exp
,那么订阅者就要产生相应的升级画面效果。
除了升级这个示例,还有像技能树,使用物品等应用。
事件的优点
- 将发布者和订阅者解耦,使它们不需要相互了解彼此的实现,这使得系统更加灵活,更容易维护和扩展。
- 事件还提供了一种统一的方式来扩展类的功能。
看到以上的优点,对于小白来说,可能不知道在讲什么,别担心,让我们继续往下看
如何创建事件
在 C#
中,事件由委托和事件关键字组合而成。那么一个基本流程如下:
- 声明一个事件时,需要先声明一个委托类型,然后使用
event
关键字将委托类型声明为事件。 - 订阅者可以通过使用
+=
运算符将其方法添加到事件的委托列表中,以便在事件发生时被调用。 - 发布者可以通过调用事件的委托列表来触发事件。
以下展示大名鼎鼎的CodeMonkey
的 C# 事件示例:
步骤一:创建发布者和订阅者的C#脚本,共同AddComponent
到同一个GameObject
步骤二:定义一个发布者
定义三个事件
OnSpacePressed
事件,该事件是一个带有OnSpacePressedArgs
参数的EventHandler
委托类型,表示按下空格键时发生的事件。OnFloatEvent
事件,该事件是一个TestEventDelegate
委托类型,表示在任何时间都可以触发的事件。OnActionEvent
事件,该事件是一个Action
委托类型,带有bool
和int
参数,表示在任何时间都可以触发的事件。
Update方法
如果检测到按下空格键,将会触发OnSpacePressed、OnFloatEvent和OnActionEvent
事件。
在OnSpacePressed
事件中(包括注释的内容),使用EventArgs.Empty
传递空参数,表示此事件不需要任何参数,但我们也可以使用自定义参数OnSpacePressedArgs
。
在OnFloatEvent
事件和OnActionEvent
事件中,使用Invoke
方法来触发事件,并将需要的参数传递给事件处理方法。
TestingEvents.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class TestingEvents : MonoBehaviour
{
// 声明一个事件
public event EventHandler<OnSpacePressedArgs> OnSpacePressed;
public class OnSpacePressedArgs : EventArgs
{
public int spaceCount;
}
private int spaceCount;
public event TestEventDelegate OnFloatEvent;
//定义一个委托类型
public delegate void TestEventDelegate(float f);
public event Action<bool, int> OnActionEvent;
//统一的安全API,方便定义委托类型
public UnityEvent OnUnityEvent;
// Start is called before the first frame update
void Start(){}
// Update is called once per frame
// 在触发事件
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
spaceCount++;
//Space pressed!
//if(OnSpacePressed!= null) OnSpacePressed(this,EventArgs.Empty);
OnSpacePressed?.Invoke(this, new OnSpacePressedArgs { spaceCount = spaceCount});
OnFloatEvent?.Invoke(5.5f);
OnActionEvent?.Invoke(true,56);
OnUnityEvent?.Invoke();
}
}
}
步骤三:实现相应的订阅者
start方法
订阅提到的三个事件,在Start
方法中,获取了TestingEvents
组件,并将其事件与TestingEventSubscriber
类中的方法关联起来,以便在事件触发时执行这些方法。这里订阅的事件分别是OnSpacePressed、OnFloatEvent和OnActionEvent
。
定义处理这三个事件方法
当事件被触发时,这些TestingEvents_OnSpacePressed、TestingEvents_OnFloatEvent和TestingEvents_OnActionEvent
方法将被调用,并按照其相应事件所期望的参数来执行。
每个方法都有一个特定的行为,例如,在TestingEvents_OnSpacePressed
方法中,会输出一个带有空格键按下次数的调试信息。
公共方法
TestingEventSubscriber
类还定义了一个名为TestingUnityEvent
的公共方法,它只输出一条调试信息,以演示如何使用方法作为事件处理程序。
TestingEventSubscriber.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestingEventSubscriber : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//作为获得订阅的行为
TestingEvents testingEvents =GetComponent<TestingEvents>();
testingEvents.OnSpacePressed += TestingEvents_OnSpacePressed;
testingEvents.OnFloatEvent += TestingEvents_OnFloatEvent;
testingEvents.OnActionEvent += TestingEvents_OnActionEvent;
}
public void TestingUnityEvent()
{
Debug.Log("TestingUnityEvent");
}
private void TestingEvents_OnActionEvent(bool arg1,int arg2)
{
Debug.Log(arg1 + " " + arg2);
}
private void TestingEvents_OnFloatEvent(float f)
{
Debug.Log("Float:" +f );
}
private void TestingEvents_OnSpacePressed(object sender, TestingEvents.OnSpacePressedArgs e)
{
Debug.Log("Space!"+e.spaceCount);
//取消订阅
//TestingEvents testingEvents = GetComponent<TestingEvents>();
//testingEvents.OnSpacePressed -= TestingEvents_OnSpacePressed;
}
// Update is called once per frame
void Update(){}
}
非小白忽略:调试看unity
的console
栏
注意事项:如何用公共方法?
先找到你的GameObject
再选择好你的公共方法
回到之前的疑惑
所谓的解耦,你可以去观察为什么发布者不用start方法?而是使用updata方法?
因为我们不希望把订阅者的实现写到发布者的类中。
公共方法其实就是个API,用就对了。
当然不要这样回答面试官问题,人家看你回答那么简单,会鄙视你的。
合作对象
强大的人工只能chatGpt3.5
参考
CodeMonkey的C# Basic视频,分析它的视频内容的是一名2$/month
的卑微学生。