委托详解
委托概念
委托是一种在对象里保存方法引用的类型,同时也是一种类型安全的函数指针。
继承自System.MulticastDelegate 特殊类-不能被继承(自己在定义类的时候)。
在IL层(中间语言,编程语言不会直接被计算机识别,会转换成中间语言(此时才是最为本质的),然后再转换成计算机识别语言)委托就是一个类。
委托的声明语法
注解:委托可以理解为是一个没有方法体的方法,因此具备方法所有的特征,如是否带返回值,是否带参数。
public delegate void NoReturnNoPara();
委托的实例化
委托的实例化 要求传递一个参数类型 返回值都跟委托一致的方法
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//DoNothing是一个方法
委托实例调用
不带参数
NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
method.Invoke();//效果等同于 this.DoNothing(); 也可以省略Invoke 直接method();
带参数
WithReturnWithPara method = new WithReturnWithPara(this.ParaReturn);
method.Invoke(out int a, ref b);//此处必须和委托声明时所要求参数个数以及类型完全一致。
泛型委托声明及调用
委托的几种调用方式
Method.Invoke();
method.BeginInvoke(null, null);//启动一个线程完成计算
method();//可以省略.Invoke
委托的意义
把方法作为参数进行传递,好处:解耦 、提高代码重用,既增加公共逻辑方便,又逻辑分离维护简单
举例场景说明
比如为中国人定义一个你好方法SayChinase,后续可能为很多国家的人定义Say方法,而且每个国家的
Say方法中都有通用的公共代码,如果按照普通的调用方法,那么应该是ClassName.Sayxx..(假设是静态方法),
这样就会导致底层代码的可复用性不强,因为你必须在每个国家的say方法里把通用的方法都写上。
这个时候我们就可以考虑使用委托,在底层定义一个委托,返回值与Say方法保持一致,再定义一个方法A
传入需要传递的参数,以及委托(这个方法里写入通用的公共代码,并将委托Invoke)。在调用时先实例化类,
再实例化委托(实例化委托时通过已经实例化的类名.方法名
把方法作为参数进行传递,再调用A方法,传入参数)
Action
提供一个不带返回值的泛型委托方法,0-16个参数
Action<sting,string.....>
具体语法
Action action=new Action(this.method);//实例化委托
//可以省略为Action action=this.method 语法糖
action.invoke();
Func
提供一个带有返回值的委托方法
Func<string,string....,返回值类型>
如何获取或者说操作Func的返回值,这里需要理解,Func实例化以后可以和正常调用方法一样得到返回值。此处需要注意,Func也是可以接收0-16参数,最后必须带有返回值类型
Sring iResult=mehtod.invoke();//接收的类型与返回类型一致即可
注:一般情况不需要再去声明委托,直接使用Action或者Func
疑问:既然可以自己声明委托,系统为什么又要提供Action和Funct?
为了统一委托规范而指定,既然系统提供了,建议直接使用系统中提供的
多播委托
多播委托的概念
所有的委托都继承自MulticastDelegate,而MulticastDelegate 就是多播委托。
多播委托中,如果每个委托多带返回参数,那么以最后一个为准,前面的丢失了。。所以一般多播委托用的是不带返回值的
多播委托存在的意义:
一个委托实例包含多个方法,可以通过+=/-=去增加/移除方法,Invoke时可以按顺序执行全部动作
语法如下
方Action<string>action=this.Do;
action+=this.Do;
Action+=this.method;
........
Action.Invoke();//会按照顺序执行系列法
+= 给委托的实例添加方法,会形成方法链,Invoke时,会按顺序执行系列方法
-= 给委托的实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除,且只移除一个,如果没有匹配,就啥事儿不发生
多播委托如果采用这种方式来调用:method.BeginInvoke(null, null);//启动一个线程完成计算
会报错,原因:多播委托实例不能异步。
如果在实际的应用场景中确实需要多播委托来异步多线程调用,可以采用如下方法实现
// foreach (Action item in method.GetInvocationList())
{
item.Invoke();//直接调用
item.BeginInvoke(null, null);//启动一个线程完成计算
}
多播委托执行出现异常
中间出现未捕获的异常,直接方法链结束了
在ASP.NETCore的核心设计就有使用到委托(无限嵌套---俄罗斯套娃),在后续关于.NETCORE的文章中会详细讲解