委托
来源:互联网 发布:矢量数据特点 编辑:程序博客网 时间:2024/06/17 04:41
熟练使用委托很有必要,但是知道委托的原理而说不清楚也是很尴尬的,尤其在面试中,在此整理一下,使描述更清晰:
首先说明委托的用法:
1、声明委托类型2、必须有一个方法包含了要执行的代码3、必须创建一个委托实例4、必须调用这个委托实例
一.委托声明与本质
1.声明委托
- 1
- 1
2.使用ILSpy反编译后,看其本质
- 1
- 1
- 编译器自动生成了一个委托类,继承自MulticastDelegate
委托被标识为class,说明委托是一种数据类型:类
委托类即可嵌套在一个类型中定义,也可以在全局范围中定义,就是说由于委托是类,所以凡是能够定义类的地方,都能定义委托;当然也可以声明委托类的变量。
委托被标识味sealed,说明是封闭类,不可以被继承
使用Invoke完成一个委托方法的封送,就类似于使用SendMessage方法来给界面线程发送消息,是一个同步方法。也就是说在Invoke封送的方法被执行完毕前,Invoke方法不会返回,从而调用者线程将被阻塞。
使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。
但是在内部实现上,Invoke和BeginInvoke都是用了PostMessage方法,从而避免了SendMessage带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。
BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。
二.创建委托变量与本质
1.既然委托是类,并且不是静态类,想要使用,就必须new
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2.反编译看两种创建变量的方式,其本质是一样的
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
编译器都会给搞成new,所以不带new的变量创建方式,是语法糖。
三.调用委托变量与本质
1.给委托变量加上()就相当于调用了
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2.反编译看其本质是调用了Invoke方法
所以我们自己也可以直接调用这个方法,委托变量调用的这两种方法,本质是一样的,编译器都会调用Invoke。简写方式也是语法糖。
- 1
- 2
- 1
- 2
3.委托变量的调用本质上通过反射对方法的调用
这是委托的构造函数,有两个参数
- 1
- 2
- 3
- 1
- 2
- 3
委托类同时提供了两个只读属性Target和Method供使用,是将委托变量指向的对象和方法进行了包装,如果方法是静态方法,则Target为null,否则就指向对象的引用。Method属性返回一个System.Reflection.MethodInfo对象的引用。在我们调用Invoke方法时,其实是执行了委托变量指向的方法,只不过有一个内部包装和调用的机制。
四.委托的好处
1.逻辑分离、解除耦合
业务逻辑未来可能会变化,不能固定,分离处理;只要把委托给我,让我调用就可以了。
2.对修改封闭、对扩展开放
未来可以给我传任何一个代理,这就是扩展。
而我不需要修改任何代码,这就是封闭。
我省事,你随意。我好你也好。
五.委托的异步调用
反编译看到委托还有两个方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
1.异步调用的本质和适合场景
这是异步调用方式,本质上是建立了一个线程,是简化的线程调用方法。
比较适合在后台运行比较耗费时间的简单任务,要求任务之间是独立的、建议任务中不有要直接访问可视化控件的逻辑
如果后台任务必须按照特定顺序执行,或者需要访问共享资源,异步编程不太适合,直接用多线程开发技术。
2.BeginInvoke跟Invoke的区别
调用Invoke,在Invoke的方法返回前,这个线程会阻塞;调用BeginInvoke,在BeginInvoke的方法返回前,这个线程不会阻塞!
六.多播委托与泛型委托
1.泛型委托变量指向多个方法并调用
委托可以声明为泛型的,一个委托变量可以指向多个方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
add委托变量指向了一个委托方法链,如果使用add()直接调用,委托方法链中的每个方法都会调用,但是返回值只能是最后一个方法。
2.反编译看起本质
- 1
- 2
- 1
- 2
调用了基类Delegate的Combine方法加入了方法链。
七.委托与匿名方法
1.使用匿名方法简化代码
如果委托指向的方法只是临时用一下,明确声明一个方法就显得啰嗦了,这个时候可以使用匿名方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
2.lambda的本质是匿名方法
反编译看,lambda表达式本质上是匿名方法,编译器自动生成了这个方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
3.委托的严格限制
方法的参数和返回值必须和委托签名保持一致。可以省略lambda的参数类型,编译器将自动推断。
- 1
- 2
- 1
- 2