委托
delegate
关键词
event
关键字,添加后将委托封装成事件。
事件和委托区别:事件相对于委托来说更加安全。
四种类型委托
// 自定义无参无返回值
public delegate void ActionX();
// 自定义有参无返回值
public delegate void ActionY(int a);
// 自定义有参无返回值
public delegate void ActionZ<T>(T a);
// 自定义有参有返回值
public delegate int ActionU(int a,int b);
class Teacher {
// (自定义无参无返回值委托)
public ActionX action1;
// (自定义有参无返回值委托)
public ActionY action2;
// (自定义有参无返回值委托)
public ActionZ<string> action3;
// (自定义有参有返回值委托)
public ActionU action4;
// 将委托封装成事件
public event ActionX action5;
// (系统自带无参无返回值委托)
public Action action6;
// (系统自带有参无返回值委托)
public Action<int> action7;
// (系统自带有参有返回值委托)
// 前面的是参数,最后一个是返回值
public Func<int, int, int> action8;
public void Test(int a,int b) {
action1?.Invoke();
action2?.Invoke(a);
action3?.Invoke(a.ToString());
action4?.Invoke(a, b);
action6?.Invoke();
action7?.Invoke(a);
action8?.Invoke(a, b);
}
}
class Teacher2 {
public Teacher2(Teacher teacher) {
teacher.action1 += Test;
teacher.action1 = Test;
teacher.action5 += Test;
// 将委托封装成事件后,外部调用只能通过 += 和 -= 进行调用不能够使用 = 但在内部可以使用
//teacher.action5 = Test;
}
private void Test() {
}
}
自定义委托类型
public delegate void Lecture();
class Teacher {
public Lecture lecture;
public Teacher() {
/*注册委托的四种方式,其中方法二和方法四较为常用,剩下两种尽量掌握,防止遇见不认识*/
// 注册委托方法一
lecture += new Lecture(action1);
// 注册委托方法二
lecture += action2;
// 注册委托方法三
lecture += delegate () { Console.WriteLine("我是注册的第三种委托,不常用"); };
// 注册委托方法四
lecture += () => { Console.WriteLine("我是注册的第四种委托,常用"); };
/*委托单播和多播*/
// 委托多播,委托方法执行,像委托中注册的多个函数都会执行,就称为多播。
lecture += action1;
lecture += action2;
// 委托单播,委托方法执行,只会执行最后等号的函数,称为单播,但若是在后面再添加其他函数后添加的函数也是会执行的
lecture = action1;
// 委托事件删除,和数组删除单个元素类似
lecture -= action1;
// 委托事件清空,和清空数组类似
lecture = null;
}
private void action1() {
Console.WriteLine("我是注册的第一种委托,不常见");
}
private void action2() {
Console.WriteLine("我是注册的第二种委托,常见");
}
}
案例一:利用委托实现玩家扣血,UI刷新功能
玩家血量逻辑
/*玩家的数据,通过向外部暴露一个委托,从而减少代码之间的耦合度*/
using UnityEngine;
// 声明一个血量变动的委托
public delegate void HpChange(float curHp, float maxHp);
public class Player : MonoBehaviour
{
// 将声明的委托进行实例化
public HpChange Hurt;
// 当前血量
float curHp;
// 最大血量
float maxHp;
void Start() {
// 初始化赋值
curHp = maxHp = 100;
// 首次执行,刷新委托,防止显示不正确
Hurt?.Invoke(CurHp,maxHp);
}
public float CurHp {
get => curHp;
set {
// 血量变动
curHp = value;
// 执行委托方法
Hurt?.Invoke(CurHp,maxHp);
}
}
// 测试逻辑
[ContextMenu("扣血")]
public void HurtTest() {
CurHp -= 10;
}
}
UI显示逻辑
/*显示UI脚本,通过向玩家的委托中注册事件,无需获得玩家的对应数据也可以获得相应的显示效果*/
using UnityEngine;
using UnityEngine.UI;
// 玩家血量UI
public class PlayerHpUI : MonoBehaviour
{
// 玩家
Player player;
// 血量Image
Image imageHp;
// 血量Text
Text textHp;
void Awake() {
// 获得要显示的UI
imageHp = GameObject.Find("HpImage").GetComponent<Image>();
textHp = GameObject.Find("HpImage/Hp").GetComponent<Text>();
// 获得对应的游戏物体
player = FindObjectOfType<Player>();
// 向游戏物体中的委托注册血量变动的方法
player.Hurt += hpShow;
}
//血量显示,当游戏物体上的委托调用,这里也会相应的调用
private void hpShow(float curHp, float maxHp) {
imageHp.fillAmount = curHp / maxHp;
textHp.text = curHp + "/" + maxHp;
}
}
案例二:做家务
using System;
namespace BigTalk {
class Program {
static void Main(string[] args) {
Mom mom = new Mom();
Dad dad = new Dad(mom);
My my = new My(mom);
mom.StartKeepHouse();
}
}
// 声明一个做家务的委托
public delegate void KeepHouse();
class Mom {
public KeepHouse KeepHouse;
public Mom() {
KeepHouse += washSheets;
}
private void washSheets() {
Console.WriteLine("我今天的家务活就是洗床单");
}
// 开始做家务
public void StartKeepHouse() {
KeepHouse?.Invoke();
}
}
class Dad {
public Dad(Mom mom) {
mom.KeepHouse += mopTheFloor;
}
private void mopTheFloor() {
Console.WriteLine("我今天的家务活就是拖地板");
}
}
class My {
public My(Mom mom) {
mom.KeepHouse += residue;
}
private void residue() {
Console.WriteLine("剩余的家务活都是我的");
}
}
}
委托的其他用法
class Program {
static void Main(string[] args) {
Teacher teacher = new Teacher();
teacher.Calculate(4,5,add);
}
private static void add(int arg1, int arg2) {
Console.WriteLine(arg1 + arg2);
}
}
class Teacher {
public void Calculate(int a, int b, Action<int, int> callback) {
callback?.Invoke(a, b);
}
}
系统自带的委托都可以通过自定义委托进行实现
开发时减少彼此代码之间的耦合度