进一步理解C#委托

前面一篇文章介绍了C#委托的基本知识(),接下来就进一步研究一下委托。

委托类型

其实,刚开始觉得委托类型是一个比较难理解的概念,怎么也不觉得下面的"AssembleIphoneHandler"是一个类型。

public delegate void AssembleIphoneHandler();

按照正常的情况,如果我们要创建一个委托类型应该是:

public class AssembleIphoneHandler : System.MulticastDelegate { }

但是,这种写法是编译不过的,会提示不能从"System.MulticastDelegate"派生子类。

其实,这里是编译器为我们做了一个转换,当我们使用delegate关键字声明一个委托类型的时候,编译器就会按照上面代码片段中的方式为我们创建一个委托类型。

知道了这些东西,对于委托类型的理解就比较容易了,通过delegate声明的委托类型就是一个从"System.MulticastDelegate"派生出来的子类。

建立委托链

下面我们通过一个例子来看看委托链的建立,以及调用列表的变化,基于前面一篇文章中的例子进行一些修改。

class Program { static void Main(string[] args) { Apple apple = new Apple(); Foxconn foxconn = new Foxconn(); Apple.AssembleIphoneHandler d1, d2, d3, d4 = null; d1 = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone); d2 = new Apple.AssembleIphoneHandler(foxconn.PackIphone); d3 = new Apple.AssembleIphoneHandler(foxconn.ShipIphone); d4 += d1; d4 += d2; d4 += d3; d4(); Console.Read(); } }

我们接下来进行一下单步调试看看委托链建立的过程。

1. 当下面三句执行完成后,可以通过VS看到d1、d2和d3的详细信息

d1 = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);

进一步理解C#委托

d2 = new Apple.AssembleIphoneHandler(foxconn.PackIphone);

进一步理解C#委托

d3 = new Apple.AssembleIphoneHandler(foxconn.ShipIphone);

进一步理解C#委托

对于上面三个委托实例来说:

调用列表为空,所以_invocationCount为0,_invocationList为空

_target代表创建委托实例的方法来自Foxconn的实例;如果是静态方法创建的委托实例_target值为null

_methodPtr代表这个方法的唯一标识,可以理解为句柄

_methodBase包含创建委托实例的方法的信息,方法名、返回类型等等

2. 通过"+="操作符来进行委托合并

d4 += d1;

进一步理解C#委托

这时,由于d4初始值为null,在使用"+="操作(Combine方法)构造委托链时,将返回另外一个参数d1,再将d1的引用赋给d4(通过"ILSpy"查看,如下图)。也就是说,这时d4将指向d1所指向的对象。

进一步理解C#委托

3. 继续执行委托合并,并查看d4的变化

d4 += d2;

进一步理解C#委托

这时可以看到调用列表的变化,_invocationList包含两个元素,分别是d1和d2.

4. 最后进行一次委托合并,把d3合并到d4中

d4 += d3;

进一步理解C#委托

可以看到最新的d4实例中,调用列表已经包含了d3。

注意:由于委托是不可变的,所以这里应该描述为,d3和d4的Combine 产生了一个新的委托实例,新的委托实例的调用列表是d3和d4的合并;操作完成后,d4变量将指向新的委托实例的引用。

疑问:其实在这步调试过程中有个疑问,_invocationCount的值是3,但是_invocationList中有四个元素,最后一个为null,找了一下也没发现为什么,望高手看到帮忙解答。

所以对委托链建立的方法Delegate.Combine(Delegate A, Delegate B),可以进行下面的概括:

如果A和B均为null,则返回null。

如果A或B一个为null而另一个不为null,则返回不为null的委托。

如果A和B均不为null,返回一个新的委托(委托是不可变的),该委托_invocationList字段为一个委托数组,该数组中委托的顺序为:A中_invacationList所指向的委托数组 + B中_invacationList所指向的委托数组。

移除委托链

我们可以通过Delegate类的静态方法Remove,从一个委托链中移除一个委托,这里就不做演示了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/56fd050e09b21e4b3e177ff304a1293f.html