言:

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

一、委托的本质

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

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

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

public delegate void DelegateTest(int parm);

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

Public class DelegateTest: System.MulticastDelegate{public DelegateTest(Object object, IntPtr method);public virtual Void Invoke(int32 parm);public virtual IAsyncResult BeginInvoke(Int32 parm, AsyncCallback callback, Object object );public virtual void EndInvoke(IAsyncResult result);}

  从中间语言的代码就可以很明显的看出我们在代码中写的委托,对于中间语言来说就是一个类,该类继承于FCL中定义的Systme.MulticastDelegate类型,所有委托类型都派生于MulticastDelegate,该类中还定义了四个方法,一个构造函数Invoke方法,还有就是两个异步方法BeginInvokeEndInvoke方法,关于这两个异步方法,大家可以查看我博客中的线程系列。大家可以用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工具下载链接的。

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

  public  class Program{// 声明一个委托类型,它的实例引用一个方法// 该方法回去一个int 参数,返回void类型public delegate void DelegateTest(int parm);public static void Main(string[] args){// 用静态方法来实例化委托DelegateTest dtstatic = new DelegateTest(Program.method1);// 用实例方法来实例化委托DelegateTest dtinstance = new DelegateTest(new Program().method2);}private static void method1(int parm){Console.WriteLine("调用的是静态方法,参数值为:" + parm);}private void method2(int parm){Console.WriteLine("调用的是实例方法,参数值为:" + parm);}}

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

二、总结

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

附件:http://down.51cto.com/data/2361648

本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1035919,如需转载请自行联系原作者

[C# 基础知识梳理系列]专题二:委托的本质论相关推荐

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

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

  2. C#基础知识梳理系列二:C#的演绎大师:类型

    C#基础知识梳理系列二:C#的演绎大师:类型 摘 要 如果说C#是CLR特邀演员阵容之一,那类型class绝对是C#的演绎/演艺大师.不朽灵魂!它不仅演绎了C#的豪放,也演艺了C#的柔美.时而恢弘.时 ...

  3. C#基础知识梳理系列十:异常处理 System.Exception

    C#基础知识梳理系列十:异常处理 System.Exception 参考文章: (1)C#基础知识梳理系列十:异常处理 System.Exception (2)https://www.cnblogs. ...

  4. Js基础知识梳理系列

    小序:总是感觉自己的技术一直在原地踏步,想学习一些新的技术,但学起来很吃力,而且总是没有什么实际的收获,似乎进入了所谓的"瓶颈期".问了一些前辈是否也遇到过同样的问题,他们给我的解 ...

  5. mysql 表 组织 管理_MySQL 基础知识梳理学习(二)----记录在页面层级的组织管理...

    1.InnoDB的数据存储结构 InnoDB中数据是通过段.簇.页面构成的. (1)段是表空间文件中的主要组织结构,它是一个逻辑概念,用来管理物理文件,是构成索引.表.回滚段的基本元素.创建一个索引( ...

  6. C#基础知识梳理系列十五:反射

    摘 要 反射,一个很有用且有意思的特性.当动态创建某个类型的实例或是调用方法或是访问对象成员时通常会用到它,它是基于程序集及元数据而工作的,所以这一章我们来讨论一下程序集.反射如何工作.如何动态创建类 ...

  7. 企业IT管理基础知识巩固系列之(二)交换机

    接上一篇<企业IT管理基础知识巩固系列之(一)路由器>所讲的迅速了解主流品牌的小方法,在中关村产品报价中,选择"交换机"品类,可以看到以下诸多品牌: 华为 锐捷网络 中 ...

  8. mysql 算子 谓词_[SQL] SQL 基础知识梳理(六)- 函数、谓词、CASE 表达式

    SQL 基础知识梳理(六)-  函数.谓词.CASE 表达式 目录 函数 谓词 CASE 表达式 一.函数 1.函数:输入某一值得到相应输出结果的功能,输入值称为"参数",输出值称 ...

  9. [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店...

    原文:[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店 一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Bytear ...

最新文章

  1. 对做C#自定义控件的一点心得
  2. anaconda卸载重装matplotlib
  3. PostSharp的AOP设计在.NET Remoting中的应用
  4. 监控服务器登录用户的操作脚本(linux下shell.sh)
  5. 84. ExtJS下页面显示中文乱码问题
  6. 风控评分模型全流程的开发及应用
  7. 2020-12-18 Simulink实现ESO(扩张状态观测器)
  8. alias怎么每次登陆都保存_alias命令使用说明
  9. leetcode 198. 打家劫舍(最简单的动态规划问题)
  10. 笑看职场什么程序员才抢手,什么样的程序员涨薪多?
  11. mysql去重取最大值,逻辑类似oracle的over(partition by)函数
  12. winform设置按钮流动_支付宝语雀上手体验:让知识流动起来
  13. 关于浏览器缓存,版本更新问题
  14. Docker 详细文档
  15. iscsi服务器搭建
  16. 国家开放大学2021春1021劳动与社会保障法题目
  17. arcengine java_浅析 ArcEngine Java - EngineViewer 例子
  18. Oracle 存储过程中,解决变量使用 in条件时,查询无效问题
  19. jQuery插件autoComplete介绍(10级学员 张帅鹏总结)
  20. ncist网络空间安全专业护网方向认知实习笔记2021.12 DAY1.2

热门文章

  1. DataGridView中的rows.Count比实际行数多1的原因以及解决办法
  2. Ecplise中怎样进行全局搜索
  3. 简单谈谈linux的文件权限问题
  4. 1、MySQL 8.0.20最新版本在Linux上安装
  5. qt 实现自己的小笔记本(哼。拿下本本记下来了
  6. 神策数据汽车行业解决方案重磅上线,全面赋能车企数字化转型
  7. 神策 FM |「聚焦」细分市场是独角兽诞生的营销关键
  8. ELK logstash 配置自定义字段为索引
  9. Cetos 7 系统安装备注事项
  10. 互联网推广方案:如何提高企业网站排名!