文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>9->>委托delegate[类型安全的回调函数]

9->>委托delegate[类型安全的回调函数]

时间:2011-04-01  来源:乱舞春秋

  1>=委托揭秘:  

  编译器和CLR在后台做了很多工作来隐藏委托本身的复杂性,如下一句委托声明:

public delegate void MyDelegate(int i);//编译器为我们产生了一个同名的类:

可以看出它默认继承自System.MulticastDelegate【所有委托都继承此类,MulticastDelegate又继承自System.Delegate】,我们声明的public,所以编译器产生的类也是public。委托可以定义在类的内部或外部,因为委托本身就是类,所以类可以定义在哪委托就可以定义在哪。System.MulticastDelegate中有几个重要的私有字段:

字段 类型 描述
_target System.object 指向回调函数被调用时应该操作的对象,用于实例方法的回调
_mothodPtr Int32 一个内部的整数值,CLR用它来标识回调函数
_prev System.MulticastDelegate 指向另一个委托对象

所有委托都有这样一个构造器<void .ctor (object,int)>,第一个参数是一个对象的引用,第二个是一个指向回调方法的整数。

声明如下一个方法:

static void myMothod(int i);

MyDelegate md=new MyDelegate(myMothod);

  我们把myMothod传给了MyDelegate的构造函数,但是这和MyDelegate构造函数的参数并不匹配,但是却编译通过了,为什么呢?因为编译器通过分析源代码来确定我们引用的哪个对象和方法,上述myMothod是静态方法,所以会把null传递给target参数,把一个标识方法的特殊Int32值【由MethodDef或者MethodRef元数据标记获得】给mothodPtr参数;myMothod是实例方法,则会把对象的引用赋给target。在构造器内部,这两个参数会被保存到相应的私有字段中。另外_prev被设置为null,该对象用来创建一个委托链表【指向下一个委托对象】。

  每个委托对象实际上是对方法及其调用时操作的对象的一个封装。System.MulticastDelegate类有两个只读的共有属性:Target和Method.当给定一个委托对象时,可以根据Target获得一个方法回调时操作的对象引用【静态方法返回null】,Method属性返回一个表示回调方法的System.Reflection.MethodInfo对象。

调用回调函数:[ md(6);]看起来像是调用一个方法似得,并且给它一个参数6。实际上并没有md方法,因为编译器知道md是一个指向委托的变量,所以他会产生代码来该委托对象的Invoke方法【让面图片最后一行】:

  md(6)会被编译为这样一行< IL_0014:  callvirt   instance void ConsoleApplication1.MyDelegate::Invoke(int32)>。但是C#不允许我们显示调用Invoke方法。

  2>=委托判等:

  Delegate重写了Object的Equals方法,判断其私有字段_target和_methodPtr字段是否指向同样的对象和方法,相同则返回true。

  MulticastDelegate又重写了Delegate的Equals方法,它又加了一项比较,就是_prev字段。如果都为null返回ture;如果都不是null,则查看_prev字段指示的链表是否有指定的长度,并且两个链表上的对应委托对象的_target和_methodPtr字段也是否匹配,如果匹配就返回ture。说白点就是Delegate的Equals判断一个委托对象是否相等,MulticastDelegate的Equals则在Delegate的基础上又增加委托链表的判断。

  3>=委托链[_prev]:

  每一个MulticastDelegate对象都有一个_prev字段,指向另一个MulticastDelegate对象的引用,则可以构成一个链表。Delegate有3个静态方法来操作委托链表

Delegate方法
 1     public abstract class Delegate : ICloneable, ISerializable
2 {
3 //创建一个由委托数组表示的委托链表
4 public static Delegate Combine(params Delegate[] delegates);
5
6 //组合a和b所代笔的链表,并返回b,
7 public static Delegate Combine(Delegate a, Delegate b);
8
9 //从source链表中移除和value匹配的委托【找不到匹配的也不抛异常】
10 //返回新的链表头部
11 public static Delegate Remove(Delegate source, Delegate value);
12
13 }

  当一个委托对象被调用时,编译器会产生Invoke方法的调用:

Invoke伪代码
1         public void virtual Invoke(int i)
2 {
3 if (_prev!=null)
4 {
5 _prev.Invoke(i);
6 }
7 _target.MethodPtr(i);
8 }

可以看出,调用一个委托对象会导致它前面的委托对象首先被调用< _prev.Invoke(i);>,当前面委托被调用时,其返回值会被丢弃。最后才会调用自己封装的回调目标< _target.MethodPtr(i);>;应用程序代码只保留了当前委托对象的哪个调用(最后一次用的回调方法)的返回值。

  注意:委托对象一旦被创建,它们就被认为ishi恒定不变的,也就是说委托对象的_prev字段总是null,并且不会改变,当调用Combine将一个新委托对象加到现有委托链中时,Combine方法内部会构造一个新的委托对象,新对象有着和源对象相同的_target和_methodPtr字段,但是其_prev字段会被指向原先委托链表的头部,最后Combine方法返回新委托对象的地址。

  移除一个委托对象<或者是它是一个委托链表>Remove方法【假如是你想要移除一个委托对象时<而不是委托链表>,但是很难看出来它到底是链表还是单独的一个委托对象。最好新创建一个相同的委托对象<新建的委托对象的_prev字段是null,这个null很有用,如下解释>】:它执行查找委托对象<或者表示一个委托链表>时,执行内部的一个判断方法【Delegate的Equals方法无法判断委托链表相等性,但是它又无法调用MulticastDelegate类的Equals<不知道这么说对不?>,所以就自己实现一个判等的方法,判等过程同MulticastDelegate类的Equals方法类似,也就是可以判断委托链表相等性了】,所以当你移除的一个委托对象恰好是一个委托链的链表头部,则会把它后面指向的委托对象一起移除掉,这恐怕不是我们愿意看到的吧。

  Remove方法每次都是从委托链表头开始移除第一个匹配项。

  未完,待续。。。


相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载