当前位置:编程学习 > C#/ASP.NET >>

.NET学习笔记(九) ------委托(下)

这一篇主要介绍委托的一些内部结构,想要了解委托的使用和功能可以看前一篇文章:.NET学习笔记(八) ------委托(上)

 前天不小心感冒了,吃了药但还是晕晕的,还是继续写把。。。

前面介绍了委托的功能和使用方法。委托实际是对一个函数指针的封装,那么他是如何指向一个函数,有是如何把多个方法都绑定起来的呢?

一 委托的内部结构

下面是简单的一个定义委托方法,写起来很简单,当编译器编译这句话的时候做了什么呢?



//申明一个委托要绑定的方法签名
public deletgate void ExampleDeletgate(int x);

通过ILDASM我们可以看到,系统产生了以下代码:



//构造器有2个参数为了方便后面使用,我重新定义这2个变量名字
//public ExamplDelegate(object target,int methodPtr)
.method public hidebysig specialname rtspecialname  
       instance 
void  .ctor(object ''object'', native int ''method'') runtime managed
{
}


//下面两个是委托的一个异步回调用方法,我们暂时不关注他们
.method public hidebysig newslot virtual 
        instance 
class [mscorlib]System.IAsyncResult 
        BeginInvoke(int32 x,
class [mscorlib]System.AsyncCallback callback,object ''object'') runtime managed
{
}


.method 
public hidebysig newslot virtual 
        instance 
void  EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
}


//这个方法和委托指定的方法原形是一样的,我同样改写下
//public void virtual Invoke(int32 x);
.method public hidebysig virtual instance void Invoke(int32 x) runtime managed
{
}

上面的代码有点乱,但是应该还是可以看出点头绪来。编译器在编译到这句的时候实际是产生了一个名字为ExampleDelegate的类,并且生成了4个方法。其中第一个是一个类型构造器,他需要2个参数。看到我重新命名的参数,大家应该猜测到他的作用了把。最后一个是一个Invoke方法,这个词是调用的意思。而且他的类型和申明的方法原形签名是相同的。大家也应该可以猜到。委托实际调用的是这个方法。而另外2个方法是和Invoke没什么区别,只是异步调用。后面文章会介绍异步调用的知识。

上面说了编译器的工作,下面我们具体来看下这个类,和其中的Invoke方法。ExampleDelegate成为了一个类,还记得定义委托对象是如何做的吗?



ExampleDelegate ed1= new ExampleDelegate(Class.method) //实例方法
 
    ExampleDelegate ed2 = new ExampleDelegate(method) //静态方法

在定义一个ExampleDelegate类型的对象的时候,我们给他传递了一个方法。这里大家肯定觉得奇怪,上面的构造方法可是需要2个参数的。我们先看看这2个参数,target 是一个object对象,他表示帮定方法的对象。而method则是表示绑定的方法。编译的时候一个参数确实也通过了。这是因为编译器知道我们在构造一个委托。所以他会自动分析我们传递给他的方法的对象。

ExampleDelegate类中有3个私有的成员变量,其中前两个提供了Target和Method 2个属性。我们可以通过这2个属性判断委托是否与一个方法绑定。

 看完了上面的3个字段应该就明白了,在定义一个委托的时候,我们的委托对象已经在内部记录了绑定的对象和要执行的方法。下面看下通过委托调用这个方法的情况



object _target;   //指向绑定方法的对象,如果是实例方法则为调用方法的对象,若为静态方法则为空



MulticaseDelegate  _prev ;  //这也是一个委托类型,用与之向下一个委托,初始为null,在.net2.0中这个字段是object类型
int _method,     //这个字段是从元数据中获得的一个值,改值是绑定的方法在元数据中的一个标识值

 

 



ed1(x); //通过委托调用绑定的方法

//下面是ILDASM中看到的
 IL_000c:  stloc.0
 IL_000d:  ldloc.
0
 IL_000e:  ldc.i4.
3
 IL_000f:  callvirt   instance 
void delegateEx.Class1/ExampleDelegate::Invoke(int32)  //实际上是调用ExampleDelegate对象的Invoke方法
 IL_0014:  ret

看到编译后的代码实际是调用了内部的Inovke函数。当调用这个方法时,会用到_target和_method字段,这就让代理在指定的对象上调用期望的方法。

 

二 委托链

在前一片文章中,我们提到过委托链这个概念,而上面内部结构中有个_prev字段就是用来指向下一个委托对象的。这就使得委托可以指向其他对象,组成一个链表结构。

 在Delegate类中提供了几个静态方法,来往一个委托链上添家或移除委托对象。



public static Delegate Combine(Delegate a, Delegate b){}

public static Delegate Combine(params Delegate[] delegates){ }

public static Delegate Remove(Delegate source, Delegate value){ }
 
public static Delegate RemoveAll(Delegate source, Delegate value){ }

下面是操作的代码:



ExampleDelegate ed1= new ExampleDelegate(method1) 
 
ExampleDelegate ed2 
= new ExampleDelegate(method2) 

    ExampleDelegate ed3 = new ExampleDelegate(method3) 

//把2个委托组成串
ExampleDelegate ed = (ExampleDelegate)Dleegati.Combine(ed1,ed2);

    ed = (ExampleDelegate)Dleegati.Combine(ed,ed3);

要注意这里ed相当于链表的头接点,而我们插入的时候使用的是头插发,就是插入到头节点后面,而不是链尾巴。所以上面最后的结果是ed--> ed3-->ed2-->ed1-->null.委托链执行的时候仍旧是执行各个委托对象内部记录的方法,只是按顺序执行。下面就看下更具体的Invoke方法



public void Invoke(int x )
{
  
if (_prve != null) _prve.Invoke(x);  //如果委托后面还有其他委托对象则执行下一个对象的Invoke方法

  _target.method(x);  
//如果委托后面没有其他委托对象,则通过内部的2个字段来执行期望的方法
}

可以看到Invoke内部是这样一个样子,所以实际上,方法是从委托链的末尾开始执行的。

(未完成)

 

 补充:asp.net教程,.Net开发

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,