C# 委托/事件/lambda
概念
委托
- 定义委托编译器会自动生成一个类派生自System.MulticastDelegate
这个类包含4个方法:一个构造器、Invoke、BeginInvoke、EndInvoke。
- 调用委托的时候实际上执行的是 Invoke方法。
MulticastDelegate类有三个重要字段:
- _target(System.Object):当委托对象包装的是一个静态方法,这个字段为null。当委托对象包装一个实例方法,这个字段引用的是回调方法要操作的对象。
- _methodPtr(System.IntPtr):这是一个
System.IntPtr
类型的字段,它存储了方法的内存地址。这个内存地址是 CLR 用来标识委托所引用方法的唯一标识。 - _invocationList(System.Object):该字段通常为null,构造委托链时它引用一个委托数组。若不为null,Invoke的时候会遍历委托数组依次调用。可以使用GetInvocationList接口获取这个数组。
用委托回调多个方法(委托链)
使用Delegate.Combine 或者用 += 可以将多个委托合并成委托链。每次添加时 _invocationList都会新建一个新的委托数组,数组里存放所有委托,之前引用的数组等待被GC回收。
使用Delegate.Remove 或者 -= 可以从委托链中删除一个委托,通过 _target和_methodPtr找到匹配的委托后,_invocationList同样会引用一个新建数组,新数组不包含移除的那个委托。
若委托链中只剩一个委托,删除成功后返回null。
C#自带的委托
1)Action:无返回值委托,最多支持16个泛型参数。
2)Func:返回一个泛型,最多支持16个泛型参数,最后一个参数必须是返回值类型。
Unity自带的委托
1)UnityAction:无返回值,最多支持4个泛型参数。
2)UnityEvent:Unity对UnityAction的封装。
泛型委托的协变/逆变参数类型
使用 in 关键字表示逆变,参数可以使用类型的派生类。
使用 out 关键字表示协变,参数可以使用类型的基类。
事件
事件是委托的包装器,内部维护了一个私有的委托链。向事件注册监听的时候其实就是往委托链里面添加一个委托,当事件触发的时候,会遍历委托链依次调用。
示例
在某个管理器中给出一个获取Sprite的方法 ,因为addressables异步加载资源,所以 获取的信息必须在资源回调中返回,那么直接使用lambda表达式便于回调的赋值
public void GetCustomerSprite(int id, Action<Sprite> callback)
{
AddressablesMgr.GetInstance().LoadAssetAsync<Sprite>(customerDataDic[id].bigImgRes, (obj) =>
{
callback(obj.Result);
});
}
外部调用代码
InventoryData.GetInstance().GetCustomerSprite(id, (sprite) =>
{
bigImg.sprite = sprite;
});