首先声明,我是一个菜鸟。一下文章中出现技术误导情况盖不负责

引言:

上一个专题已和大家分享了我懂得的——C#中为什么须要委托,专题中简略介绍了下委托是什么以及委托简略的应用的,在这个专题中将对委托做进一步的介绍的,本专题主要对委实质和委托链停止讨论。

一、委托的实质

平常我们很容易应用委托——用C# delegate关键字定义委托,再用new操作符结构委托实例,然后通过调用委托实例来调用回调方法(就是用一个了委托对象的变量来取代方法名,这句话如果刚接触的人欠好懂得的话,这里给个例子:MyDelegate mydelegate =new Mydelegate(obj.mymethod),MyDelegate是定义的一个委托,假设定义的是没有参数的,然后调用委托实例是这样的——mydelegate(), 大家可以发现此时调用委托和调用方法的方法是如出一辙的,如果没有看前面mydelegate是个委托类型,大家都会以为这是直接调用一个方法,而不是调用委托实例,通过这个例子大家应当很容易明确了这句话了吧——用一个委托对象的变量来取代方法名),相信通过括号内的讲授后,相信大家又会对委托有进一步的懂得的——委托就是方法的取代品,委托变量此时着方法名,大家可以简略懂得委托是方法的一个 “外号”。

前面的都介绍了委托的一些应用和懂得的,现在就让我我们来进一步看看编译器和CLR在背后对我们用delegate 关键字定义的委托类型做了些什么事情的,前一个专题中我和大家说过委托是一个类,这么是有根据的,因为我们在IDE中定义一个委托类型时,最终是通过编译器将定义的代码转化为中间语言IL,然后再执行中间语言中的代码来转化为本机代码的,所以在Visual Studio中编写的代码只是一个包装而已,真真程序执行的是中间语言中的代码的。现在就看看编译器把我们定义的委托类型转化为什么样的中间语言代码的。

当我们在类中像上面这样定义一个委托时:

  1. public delegate void DelegateTest(int parm);

编译器把我们定义的委托类型编译成一个上面这样的类:

  1. Public class DelegateTest: System.MulticastDelegate
  2. {
  3. public DelegateTest(Object object, IntPtr method);
  4.  
  5. public virtual Void Invoke(int32 parm);
  6.  
  7. public virtual IAsyncResult BeginInvoke(Int32 parm, AsyncCallback callback, Object object );
  8.  
  9. public virtual void EndInvoke(IAsyncResult result);
  10.  
  11. }

从中间语言的代码就可以很明显的看出我们在代码中写的委托,对于中间语言来说就是一个类,该类继承于FCL中定义的Systme.MulticastDelegate类型,所有委托类型都派生于MulticastDelegate,该类中还定义了四个方法,一个结构函数,Invoke方法,还有就是两个异步方法BeginInvoke和EndInvoke方法,关于这两个异步方法,大家可以查看我博客中的线程系列。大家可以用ILDasm.exe工具去查看委托生成的中间代码,上面我截的一个图(从我们定义的DelegateTest的前面的图标和我们主程序传递Program的图标是一样的,然而Program是一个类,很明显定义的委托DelegateTes也是一个类的):

由于所有委托类型都是继承于MulticastDelegate,MulticastDelegate又继承于Delegate,所以委托类型继承了MulticastDelegate的字段、属性和方法,在这些成员中,有三个非公共字段与后面专题要介绍的委托链有关,所以在这里先列出来的:

    每日一道理
喜马拉雅直冲霄汉,可上面有攀爬者的旗帜;撒哈拉沙漠一望无垠,可里面有跋涉者的脚印;阿尔卑斯山壁立千仞,可其中有探险者的身影;雅鲁藏布江湍急浩荡,可其中有勇敢者的故事。

字段

类型

解释

_target

System.Object

当委托对象包装的是一个静态方法时,这个字段为null,当委托对象包装一个实例方法时,这个字段引用的是方法地点的类的对象

_methodPtr

System.IntPtr

一个外部的整数,可以以为是方法句柄,标识着要调用的方法

_invocationList

System.Object

该字段平日为null,当结构一个委托链(多播委托)时,才引用一个委托数组。具体下一部分讲授。

大部分人可能会有这么个疑难,既然是非公共字段,所以在MSDN上是看不到的,那我是怎么晓得有这三个字段的呢?大家可以通过Reflector工具是反编译查看源码,Multicastdelegate 类通过MSDN查找可以晓得该类的命名空间和程序集,这样就可以更具程序集和命名空间用Reflector工具查看Multicastdelegate类的源码,上面是我用Reflector这个工具查看到的源码截图:

从截图中可以看出MulticastDelegate 类中只有两个字段,却没有前面表格中列出的_methodPtr和_target字段的,这两个字段是定义在Delegate类中,大家应用Reflector工具来查看的,这里就不具体贴图了,文章最后会给出Reflector工具下载链接的。

委托对象就是一个包装器,包装了一个方法和调用该方法时要操作的对象,例如,执行上面的代码时:

  1. public class Program
  2. {
  3. // 声明一个委托类型,它的实例引用一个方法
  4. // 该方法归去一个int 参数,返回void类型
  5. public delegate void DelegateTest(int parm);
  6.  
  7. public static void Main(string[] args)
  8. {
  9. // 用静态方法来实例化委托
  10. DelegateTest dtstatic = new DelegateTest(Program.method1);
  11.  
  12. // 用实例方法来实例化委托
  13. DelegateTest dtinstance = new DelegateTest(new Program().method2);
  14. }
  15. private static void method1(int parm)
  16. {
  17. Console.WriteLine("调用的是静态方法,参数值为:" + parm);
  18. }
  19.  
  20. private void method2(int parm)
  21. {
  22. Console.WriteLine("调用的是实例方法,参数值为:" + parm);
  23. }
  24. }

代码中dtstatic 和dtinstance变量引用了初始化好的DelegateTest委托对象,此时这两个委托对象的上面列出来的三个字段初始化情况如下图:

二、总结

本专题从中间语言的角度去详细剖析定义的委托类型经编译器转化后的的中间语言是怎样来解释一个委托类型的,得到的结论是——委托实际上是一个类,该类派生于MulticastDelegate类,且继承了该类的_target,_methodPtr和_invocationList这三个字段,当我们初始化一个委托对象时,此时就会先初始化这三个字段,对于包装实例方法和静态方法的委托,初始化这三个字段也有所不一样,在上面的截图中也所体现,这里引用了一个很重要的字段——_invocationList(即委托实例的调用列表),对于委托对象包装一个方法时,该字段为null,如果委托对象要包装多个方法时,此时_invocationList字段就会被初始化为引用一个委托对象的数组(就是指向委托对象的一个集合),具体这方面的内容将在下一专题介绍委托链中为大家详细介绍。 到这里,本专题的内容也结束了,希望通过本专题,大家可以更进一步的懂得C#中的委托。

Reflector工具的下载地址:http://files.cnblogs.com/zhili/Reflector.zip ,看完后认为有帮助的话,请大家多多赞下的,谢谢大家的支持。

文章结束给大家分享下程序员的一些笑话语录: 这个世界上只有10种人:懂得二进制的和不懂得二进制的。

---------------------------------
原创文章 By
方法和字段
---------------------------------

方法字段[C# 基础知识系列]专题二:委托的本质论的更多相关文章

  1. [C# 基础知识系列]专题二:委托的本质论 (转载)

    引言: 上一个专题已经和大家分享了我理解的——C#中为什么需要委托,专题中简单介绍了下委托是什么以及委托简单的应用的,在这个专题中将对委托做进一步的介绍的,本专题主要对委本质和委托链进行讨论. 一.委 ...

  2. [C# 基础知识系列]专题三:如何用委托包装多个方法——委托链 (转载)

    引言: 上一专题介绍了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的介绍都是委托只是封装一个方法,那委托能不能封装多个方法呢?因为生活中经常会听到, ...

  3. 链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链

    最近研究链方法,稍微总结一下,以后继续补充: 弁言: 上一专题分析了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的分析都是委托只是封装一个方法,那委 ...

  4. [C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托

    转自http://www.cnblogs.com/zhili/archive/2012/10/22/Delegate.html 引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不 ...

  5. [C# 基础知识系列]专题九: 深入理解泛型可变性

    引言: 在C# 2.0中泛型并不支持可变性的(可变性指的就是协变性和逆变性),我们知道在面向对象的继承中就具有可变性,当方法声明返回类型为Stream,我们可以在实现中返回一个FileStream的类 ...

  6. [C# 基础知识系列]专题四:事件揭秘

    转自http://www.cnblogs.com/zhili/archive/2012/10/27/Event.html 引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听 ...

  7. [C# 基础知识系列]专题四:事件揭秘 (转载)

    引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到“事件”这个概念的,尤其是写UI的时候,当我们点击一个按钮后VS就会自动帮我们生成一些后台的代码,然后我们就只需要在Cl ...

  8. [C# 基础知识系列]专题八: 深入理解泛型(二)

    引言: 本专题主要是承接上一个专题要继续介绍泛型的其他内容,这里就不多说了,就直接进入本专题的内容的. 一.类型推断 在我们写泛型代码的时候经常有大量的"<"和"& ...

  9. [C# 基础知识系列]专题七: 泛型深入理解(一) (转载)

    引言: 在上一个专题中介绍了C#2.0 中引入泛型的原因以及有了泛型后所带来的好处,然而上一专题相当于是介绍了泛型的一些基本知识的,对于泛型的性能为什么会比非泛型的性能高却没有给出理由,所以在这个专题 ...

随机推荐

  1. 庖丁解牛-----Live555源码彻底解密(RTP打包)

    本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...

  2. Java 单元测试(Junit)

    在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试.当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候 ...

  3. store / cache 系列

    ### golang go-cache An in-memory key:value store/cache (similar to Memcached) library for Go, suitab ...

  4. C#主线程等待子线程运行结束

    佐左佑右 原文 C#主线程等待子线程运行结束 由于主程序中调用matlab的dll文件进行计算要用较长的时间,主界面会有很长时间的卡顿,造成的用户感受十分不好,因此我想在调用时,将调用放入子线程中,然 ...

  5. Android中GridView滚动到底部加载数据终极版

    之前在项目中有一个需求是需要GridView控件,滚动到底部自动加载.但是呢GridView控件并不提供诸如ListView监听滚动到底部的onScrollListener方法,为了实现这样一个效果, ...

  6. C++继承与派生的概念、什么是继承和派生

    在C++中可重用性是通过继承(inheritance)这一机制来实现的.因此,继承是C++的一个重要组成部分. 前面介绍了类,一个类中包含了若干数据成员和成员函数.在不同的类中,数据成员和成员函数是不 ...

  7. 检查ept

    cat /proc/cpuinfo | grep ept                                检查cpu是否支持ept cat /sys/module/kvm_intel/p ...

  8. HDU 1536 sg-NIM博弈类

    题意:每次可以选择n种操作,玩m次,问谁必胜.c堆,每堆数量告诉. 题意:sg—NIM系列博弈模板题 把每堆看成一个点,求该点的sg值,异或每堆sg值. 将多维转化成一维,性质与原始NIM博弈一样. ...

  9. File-nodejs

    文件系统模块是一个简单包装的标准 POSIX 文件 I/O 操作方法集.您可以通过调用require('fs')来获取该模块.文件系统模块中的所有方法均有异步和同步版本. 文件系统模块中的异步方法需要 ...

  10. Google AppEngine 创建的例子

    1.guestbook: http://chenyy-gac-test.appspot.com/ 2.time clock: http://chenyy-gac-20150922.appspot.co ...