MSIL入门(四)之委托delegate

  • A+
所属分类:.NET技术
摘要

委托是一种特殊的引用类型,其设计目的是表示函数指针。所有委托都来自[System.Runtime]System.MulticastDelegate类型,它又派生自[System.Runtime]。委托本身是密封的(就像值类型一样),因此不能从它们派生类型。

委托是一种特殊的引用类型,其设计目的是表示函数指针。所有委托都来自[System.Runtime]System.MulticastDelegate类型,它又派生自[System.Runtime]。委托本身是密封的(就像值类型一样),因此不能从它们派生类型。

对委托结构施加的限制与对枚举器结构施加的限制一样严格。委托没有字段、事件或属性。它们只能有两个或四个实例方法,并且这些方法的名称和签名是预定义的。

委托的两个强制方法是实例构造函数(.ctor)和Invoke。实例构造函数返回void并接受两个参数;对定义被委托方法的类型的对象引用和对被委托的托管方法的函数指针。

这就引出了一个问题:如果你能得到一个函数指针,为什么你还需要委托呢?为什么不直接使用函数指针?可以,但是需要引入函数指针类型的字段或变量来保存这些指针——函数指针类型被认为是安全风险(因为指针值在从特定函数获得后可以修改),并且被认为是不可验证的。如果一个模块是不可验证的,它在禁用所有安全检查时,只能从本地驱动器以完全信任模式执行。另一个缺点是,在调用非托管方法时,托管函数指针不能封送到非托管函数指针,而委托可以封送。

第二个强制方法(Invoke)必须与委托方法具有相同的签名。两个强制的方法(.ctor和Invoke)足以允许委托用于同步调用,这是在调用线程被堵塞直到被调用方法返回之前通常的方法调用。第一个方法(.ctor)创建委托实例,并将其绑定到被委托的方法。Invoke方法用于对delegate方法进行同步调用。

当被调用的方法在公共语言运行时为此目的而创建的单独线程上执行并且不阻止调用线程时,委托还可以用于异步的调用。为了能够被异步调用,委托必须定义两个额外的方法:BeginInvokeEndInvoke

BeginInvoke是线程启动程序,它接受委托方法的所有参数加上另外两个参数:类型为[System.Runtime]的委托System.AsyncCallback表示在调用完成时调用的回调方法,以及选择用于指示调用的最终状态的对象线程。BeginInvoke返回接口[System.Runtime]的实例System.IAsyncResult,携带作为最后一个参数传递的对象,需要记住的是由于接口、委托和对象都是引用类型,所以当我说“接受委托”或“返回接口”时,实际指的是引用。

如果我们打算在调用完成时立即受到通知,则必须指定AsyncCallback委托。在异步调用完成时调用相应的回调方法。这种事件驱动技术是对异步调用完成作出反应的最广泛使用的方法。

我们可以选择另一种方法来监视异步调用线程的状态:主线程轮询。返回的接口具有bool get_IsCompleted()方法,当异步调用完成时,该方法返回true,我们可以不时地从主线程调用此方法,以确定调用是否已经完成。

我们可以调用返回接口的另一个方法get_AsyncWaitHandle,它返回一个等待句柄,一个[System.Runtime]System.Threading的实例。WaitHandle类,在获得等待句柄之后,我们可以用任意自己喜欢的方式监视它(类似于Win32 api WaitForSingleObject和WaitForMultipleObjects的使用)。

如果选择使用轮询技术,则可以放弃回调函数并指定null而不是System.AsyncCallback委托实例。

EndInvoke方法采用[System.Runtime]System.IAsyncResult接口,由BeginInvoke作为其单个参数返回,并返回void。此方法等待异步调用完成,从而阻塞调用线程,因此在BeginInvoke之后立即调用它相当于使用Invoke的同步调用。最终必须调用EndInvoke才能清除相应的运行时线程表条目,但应该在知道异步调用已完成时执行。

如果选择使用轮询技术,则可以放弃回调函数,而指定null而不是System.AsyncCallback委托实例。

EndInvoke方法采用[System.Runtime] System.IAsyncResult接口,由BeginInvoke返回,作为它的单个参数,并返回void。这个方法等待异步调用完成,阻塞了调用线程,因此在BeginInvoke之后立即调用它等同于使用Invoke进行同步调用。最终必须调用EndInvoke,以清除相应的运行时线程表条目,但应该在知道异步调用已经完成时执行。

委托的四个方法都是虚拟的,它们的实现由CLR本身提供的,用户不需要边写这些方法的主体。在定义delegate时,我们可以简单的声明方法而不提供实现,如下所示:

 .class nested public sealed auto ansi     MyDelegate       extends [System.Runtime]System.MulticastDelegate   {      .method public hidebysig specialname rtspecialname instance void       .ctor(         object 'object',         native int 'method'       ) runtime managed     {       // Can't find a body     } // end of method MyDelegate::.ctor      .method public hidebysig virtual newslot instance int32       Invoke(         string s       ) runtime managed     {       // Can't find a body     } // end of method MyDelegate::Invoke      .method public hidebysig virtual newslot instance class [System.Runtime]System.IAsyncResult       BeginInvoke(         string s,         class [System.Runtime]System.AsyncCallback callback,         object 'object'       ) runtime managed     {       // Can't find a body     } // end of method MyDelegate::BeginInvoke      .method public hidebysig virtual newslot instance int32       EndInvoke(         class [System.Runtime]System.IAsyncResult result       ) runtime managed     {       // Can't find a body     } // end of method MyDelegate::EndInvoke   } // end of class MyDelegate 
    class Program     {         public delegate int MyDelegate (string s);                  static void Main(string[] args)         {                     }              } 

参考:《Expert .NET 2.0 IL Assembler - Serge Lidin》