通过IL分析C#中的委托、事件之间的区别与联系

一直以来都是对于事件与委托比较混淆,而且不太会用。找了个时间,总结了一下,感觉清晰了很多。

先说一下个人理解的结论吧:

delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。

delegate声明的变量与delegate声明的事件,并没有本质的区别,事件是在delegate声明变量的基础上包装而成的,类似于变量与属性的关系(在IL代码中可以看到每一个delegate声明的事件都对应是私有的delegate声明的变量),提升了安全性。

首先了解一下, ILDasm中图标含义:

该图来自:http://www.cnblogs.com/zery/p/3366175.html

新建一个事件委托测试项目:EventDelegateTest

具体代码如下:

<span style="font-size:14px;">namespace EventDelegateTest
{
    public class TestClass
    {
        public delegate int delegateAction();
        public event delegateAction OnActionEvent;
        public delegateAction daNew;
    }
}</span>

编译代码后,使用 Visual Studio 2010自带的ILDASM.EXE:

打开该dll,可以看到如下信息:

从上图可以看出如下几点信息:

1、委托 public delegate int delegateAction();

在IL中是以类(delegateAction)的形式存在的

2、public event delegateAction OnActionEvent;

在IL中不仅仅对应event OnActionEvent而且还对应一个field OnActionEvent;

而field OnActionEvent与 public delegateAction daNew生成的field daNew是一样的

都是以字段(field )的形式存在的。

双击event OnActionEvent可以看到如下信息:

在IL中事件被封装成了包含一个add_前缀和一个remove_前缀的的代码段。

其中,add_前缀的方法其实是通过调用Delegate.Combine()方法来实现的,组成了一个多播委托;remove_就是调用Delegate.Remove()方法,用于移除多播委托中的某个委托。

也就是说:事件其实就是一个特殊的多播委托

那么对于事件进行这一次封装有什么好处呢?

1、因为delegate可以支持的操作非常多,比如我们可以写onXXXChanged += aaaFunc,把某个函数指针挂载到这个委托上面,但是我们也可以简单粗暴地直接写onXXXChanged = aaaFunc,让这个委托只包含这一个函数指针。不过这样一来会产生一个安全问题:如果我们用onXXXChanged = aaaFunc这样的写法,那么会把这个委托已拥有的其他函数指针给覆盖掉,这大概不是定义onXXXChanged的程序员想要看到的结果。

小注:

虽然事件不能直接=某个函数,但可以=null,此处需要注意

2、还有一个问题就是onXXXChanged这个委托应该什么时候触发(即调用它所包含的函数指针)。从面向对象的角度来说,XXX改变了这个事实(即onXXXChaned的字面含义)应该由包含它的那个对象来决定。但实际上我们可以从这个对象的外部环境调用onXXXChanged,这既产生了安全问题也不符合面向对象的初衷。

参考文章:http://www.zhihu.com/question/28932542

时间: 2024-11-04 04:13:25

通过IL分析C#中的委托、事件之间的区别与联系的相关文章

通过IL分析C#中的委托、事件、Func、Action、Predicate之间的区别与联系

一直以来都是对于事件与委托比较混淆,而且不太会用.找了个时间,总结了一下,感觉清晰了很多. 先说一下个人理解的结论吧: delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类. delegate声明的变量与delegate声明的事件,并没有本质的区别,事件是在delegate声明变量的基础上包装而成的,类似于变量与属性的关系(在IL代码中可以看到每一个delegate声明的事件都对应是私有的delegate声明的变量),提升了安全性. Action 与Func:这两个其实

shell中;;、&、&&之间的区别

;;: command1;command2... --不管command1命令是否执行成功,command2命令都执行. &: command1&command2... --command1命令和command2命令同时执行 &&: command1&&command2... --只在command1命令执行后,才执行command2命令. shell中;;.&.&&之间的区别

C#中的委托事件的分析

推荐:http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html 委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里堵得慌,浑身不自在.本章中,我将由浅入深地讲述什么是委托.为什么要使用委托.对它们的编译代码也做了讨论 using System; usin

Spark源码分析:多种部署方式之间的区别与联系(1)

从官方的文档我们可以知道,Spark的部署方式有很多种:local.Standalone.Mesos.YARN.....不同部署方式的后台处理进程是不一样的,但是如果我们从代码的角度来看,其实流程都差不多. 从代码中,我们可以得知其实Spark的部署方式其实比官方文档中介绍的还要多,这里我来列举一下: 1.local:这种方式是在本地启动一个线程来运行作业: 2.local[N]:也是本地模式,但是启动了N个线程: 3.local[*]:还是本地模式,但是用了系统中所有的核: 4.local[N

Java中equals和==之间的区别

今天在写表达式求值的时候,发现了equals和==||!=和!equals()之间是不一样的. 我就从网上搜了搜关于这方面的知识,然后在下面做一个总结: Java中有两类数据类型: 基本数据类型(Primitive Data Types) 其中:byte,short,char,int,long,double,boolean 他们用(==).(!=).(.equals())这三种方法进行比较的时候都是比较值 复合数据类型(Composite Data Types) 其中:String,数组,一般的

Labview中定时函数之间的区别

第一个定时函数(时间延迟):在VI中插入时间延迟,指定在运行调用VI之前延时的秒数.默认值为1.000. 第一个定时函数(等待(ms)):等待指定长度的毫秒数,并返回毫秒计时器的值.该函数进行异步系统调用,但函数节点却是同步操作的.所以,直至指定时间结束,函数才停止执行:LabVIEW调用VI时,如毫秒计时值为112毫秒,等待时间(毫秒)为10毫秒,则毫秒计时值为122毫秒时,VI执行结束. 等待直至毫秒计时器的值为毫秒倍数中指定值的整数倍.该函数用于同步各操作.在循环中调用该函数可控制循环执行

jQuery中的$()和javascript中的getElementById()之间的区别

jQuery的成功多归功于其强大的选择器. 然而,相信不少初学jQuery的同学都会遇到下面的问题. 在javascript下,我们可以根据getElementById()来获取页面元素.如下: var oDiv1 = document.getElementById("divId"); 用jQuery就方便多了: var oDiv2 = $("#divId"); 一般我们会认为,这两个东西返回的都是一个ID为"divId"的页面元素对象.但是事实

JS 中的事件绑定、事件监听、事件委托

事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 在DOM中直接绑定事件 我们可以在DOM元素上绑定onclick.onmouseover.onmouseout.onmousedown.onmouseup.ondblclick.onkeyd

keydown,keypress,keyup三者之间的区别

最近看了Javascript高级教程中对过滤输入的介绍,想实现比如电话号码中不能包好非数值的字符,而相应文本中插入字符的操作是keypress事件,所以就想通过阻止这个事件的默认事件行为来阻止这个事件的默认行为来屏蔽此类字符,但是屏蔽之后所有的安检操作都会被屏蔽,文本框会变成可读的.如果只想屏蔽特定的字符,需要检测keypress事件对应的字符编码,然后决定如何响应. 但是我在利用keypree事件的时候,完全搞蒙了.因为前面看了keydown.keypress.keyup事件,它们到底怎么用呢