“幻想:如果能有一种能把方法当参数的方法就好了”
一、什么是委托
委托源自C、C++中的函数指针
1.C语言中的函数指针
(1)函数的直接调用
先准备好一个加法函数,一个减法函数
可以通过函数名调用这两个函数,这种调用方法称作直接调用
(2)函数指针的声明、间接调用
先按下图中的方法声明一个函数指针数据类型
然后创建一个这个数据类型的指针变量,将Add函数的地址存入其中
之后不再直接调用这两个函数,而是通过函数指针来间接调用他们
间接调用与直接调用结果是完全一样的,因为无论直接和间接,CPU访问的那组包含算法的机器语言是一样的
(3)一切皆地址
数据(变量)的存在内存中的,算法(函数)也是存在内存中的
所以其在内存中存储的地方都有地址
(4)C#与JAVA
JAVA是由C++演化来的,为程序安全性考虑,JAVA禁止直接访问内存地址,彻底舍弃了所有与指针相关的内容
C#也是由C++演化而来,但通过委托保留了C++中的函数指针内容
二、Action委托(无参无返回值)
使用Action这种数据类型,其图标为一个手提包,意为打包装着一些方法
Action委托类型的变量的初始器,可以在括号内写上要塞进委托里的方法
括号内只写方法名,不写圆括号,因为写上圆括号意为调用方法
直接调用方法:如往常一样
用委托间接调用方法
写法1 action.Invoke()
通过action.Invoke()方法调用被装在委托实例action中的方法
直接调用与间接调用效果完全一样
写法2 action();
模仿函数指针的简便调用写法,直接把委托实例当函数使用
三、Func泛型委托(泛型委托,声明需写上参返类型)
Func是function的缩写,这是一种泛型委托
(感觉写法类似unity里的获取组件那样的泛型方法?)
1.Func泛型委托的声明
他有多种重载,不同重载有不同数量的类型参数< >
这些尖括号类型参数< >,表示的是目标方法的参数类型
我们现在要把Add方法和Sub方法塞进这个Func委托,所以选择这个重载,有T1、T2两个int类型参数,有TResult这一个返回值
所以在尖括号中依次写上参数和返回值的类型(写法类似获取组件那样的泛型方法)
将要塞进委托里的方法名写在初始化器的圆括号内
2.Func泛型委托的调用
(1)用func.Invoke(···)方法调用
圆括号内写上参数
(2)用类似函数指针的func(···);调用
圆括号内写上参数,就像普通调用方法一样
四、自定义委托
委托是一种类Class,但考虑到可读性和C、C++传统,其声明方法仿照函数指针
委托作为类也要声明在命名空间体中
声明委托delegate
delegate表示声明的是一个委托,之后写上目标方法的返回值、委托名、目标方法参数列表
委托的初始化与调用
创建自定义委托类型的变量后,需要用初始化器创建委托实例
委托的初始化器括号中写要塞进委托的方法
能塞进去的方法必须满足委托声明时定下的返回值类型、参数列表的要求
Invoke间接调用
函数指针式调用
委托与所封装的方法必须保持“类型兼容”,返回值类型、参数列表类型一致
五、委托的一般使用
常用实例:委托参数
使用委托作为方法的参数,将委托封装的方法传入方法体,在方法体中间接调用
形成一种能够动态调用方法的“活字印刷术”
1.模板方法
通过委托参数,间接的在方法体中调用指定的外部方法,作为委托参数被调用的方法一般是有返回值的方法,然后使用其返回值继续执行方法体中的逻辑
相当于方法中留空,可以动态地在留空处塞外部方法,来活字印刷
2.回调方法(callback)
某个方法用得到的时候就用,不用的时候就不用
将委托参数传入主调方法,委托参数内封装着回调方法
主调方法根据自己的逻辑来判断是否调用回调方法,一般在主调方法的末尾,执行一些后续工作
因为在末尾,没后续,所以回调方法一般是没有返回值的
示例:模板方法与包装工厂
暂时无法在飞书文档外展示此内容
这样当一个产品的生产程序出问题时,不会干扰其他产品的生产和包装 需要维护一个产品的生产程序时,也不需要动其他产品的生产和包装程序 提高维护性、可扩展性
示例2:回调方法与记录者
回调方法也称作好莱坞方法
演员去面试,给导演留了名片
导演说如果选中他,会给他打电话的
暂时无法在飞书文档外展示此内容
后:回调方法就是不每一帧检测,等事件来触发
委托注意事项
委托是一种方法级别的耦合,通常是违反设计模式的(后:可使用观察者模式/事件消息系统解决此问题)
会引起可读性急剧下降
把委托回调、异步调用和多线程纠缠在一起,会对代码的可读性和维护性产生灾难性的后果
委托使用不当可能造成内存泄露、程序性能下降