转载一下自己记录
对委托相关知识的总结,参考了一些博客再加上了自己的理解。
delegate、Func、Action、event、UnityEvent、UnityAction
委托
一种可用于封装命名或者匿名方法的引用类型。
它的存在说明变量不仅能存储值,对象的引用,还能存方法。只不过声明时前面加个delegate。与函数指针不是同个概念。
类似于 C++ 中的函数指针,而且是类型安全和可靠的。
可用于简化代码,实现观察者模式。
一、自定义委托类型:
delegate 返回值 方法名(参数)
1
二、.NET自带泛型委托类型:
省去自定义委托类型
1、Action< T >:只能委托无返回值的方法
2、Func< T , Result >只能委托有返回值的方法,可有参数数量:0~5
// 一、显式委托声明 – 定义一个签名:
delegate double MathFunc(double num);
class DelegateTest
{
// 符合委托声明的常规方法
double Double(double input)
{
return input * 2;
}
void Output(double result)
{
Console.WriteLine(result);
}
void Main()
{
// 1、使用一个命名方法实例化委托类型
MathFunc mf1 = Double;
// 调用委托实例
double res = mf1(233);
// 2、用匿名方法来实例化委托类型
mf1 = delegate(double input)
{
return input * input;
};
res = mf1(233);
// 3、用Lambda表达式来实例化委托类型
mf1 = s => s * s * s;
res = mf1(4);
//二、1、实例化Func、Action委托类型(省去显式自定义委托)
Func<double, double> mf2 = Double;
Action<double> mf3 = Output;
// 2、用匿名方法
mf2 = delegate(double input)
{
return input * input;
};
// 3、用Lambda表达式
mf2 = s => s * s * s;
res = mf2(233);
}
}
三、多重委托:event
相当于一个方法的容器,调用event时,便调用容器中的所有方法。因此可用于实现观察者模式。
可用+=、-=增删相应的委托类型的方法。直接以方法的形式调用,或者以Invoke()调用。
注意,在方法内部声明event会报错。
示例代码:
class EventTest
{
//1、声明包含Func类型的委托的event
event Func<double, double> mfe;
void Main()
{
Func<double, double> ma1 = s => s * s;
Func<double, double> ma2 = s => s * s * s;
//2、增加包含的方法
mfe += ma1;
mfe += ma2;
//3、调用所有方法
double res = mfe(233);
}
}
使用event实现发布-订阅模式:
Idol.cs:发布者
using UnityEngine;
public class Idol : MonoBehaviour {
public delegate void IdolBehaviour(string behaviour);
//设置为静态以让粉丝们能订阅、退订(增删委托方法)
public static event IdolBehaviour IdolDoSomethingHandler;
private void Start()
{
//Idol 决定搞事了, 如果他还有粉丝的话, 就必须全部都通知到
if (IdolDoSomethingHandler != null)
{
//所有的粉丝都接收到消息(参数),并行动(执行)
IdolDoSomethingHandler("Idol give up writing.");
}
}
}
SubscriberA.cs:订阅者A
using UnityEngine;
public class SubscriberA : MonoBehaviour {
///
/// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
///
private void OnEnable()
{
//粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
Idol.IdolDoSomethingHandler += LikeIdol;
}
///
/// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
///
private void OnDisable()
{
Idol.IdolDoSomethingHandler -= LikeIdol;
}
///
/// 粉丝A是一个脑残粉
///
///
public void LikeIdol(string idolAction)
{
print(idolAction + " I will support you forever!");
}
}
SubscriberB.cs:订阅者B
using UnityEngine;
public class SubscriberB : MonoBehaviour {
///
/// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
///
private void OnEnable()
{
//粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
Idol.IdolDoSomethingHandler += HateIdol;
}
///
/// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
///
private void OnDisable()
{
Idol.IdolDoSomethingHandler -= HateIdol;
}
///
/// 粉丝B是一个无脑黑
///
///
public void HateIdol(string idolAction)
{
print(idolAction + " I will hate you forever!");
}
}
四、Unity的再包装:UnityAction、UnityEvent
Unity独有的这两个类分别对应Action,event。与Event不同的是,UnityEvent序列化后,可以在Editor面板上显示,增删包含的方法(UnityAction)。
Unity所使用的监听器(例如OnClick)都是UnityEvent类。
作为一个对象,而不是一个有特殊待遇的event。它要按对象的方式来使用。可继承。
AddListener():注册UnityAction
RemoveListener:取消注册UnityAction
Invoke():调用所有UnityAction
示例代码:
using UnityEngine;
using UnityEngine.Events;
//使用Serializable序列化IdolEvent, 否则无法在Editor中显示
[System.Serializable]
public class IdolEvent : UnityEvent<string> {
//......
}
public class Idol : MonoBehaviour {
public IdolEvent idolEvent;
private void Start()
{
if (idolEvent == null)
{
idolEvent = new IdolEvent();
}
idolEvent.Invoke("Idol give up writing.");
}
}