在谈论主题之前,让我们先简单回顾下事件的基础知识吧!
我们知道事件有发出(raises)事件的源,即event sender,也有接收事件通知(notifications)的接收者,即event receiver。因此,若要设计事件必须从发送者和接受者两个Object分别着手设计。其中,发送方类型的设计一般有四个步骤:
- 定义类型来容纳所有需要发送给事件通知接收者的信息。
- 定义事件成员。注意这里事件成员的类型可以是字段或属性,定义成不同的类型,编译生成的代码有较大的不同,需注意。
- 定义负责引发事件的方法来通知事件的登记对象。
- 定义一个方法将输入转化为期望的事件。注意,如果在发起事件时事件发送者需要传递信息,那么需要定义这个方法,将输入传递给封装的EventArgs对象,然后调用步骤3的方法,并将EventArgs对象传递给该方法。如果不需要传递信息则不需定义该方法,直接调用步骤3中的方法即可。
接收方类型设计一般有三个步骤:
- 设计接收方类型的事件处理方法。
- 设计接收方类型的注销对事件关注的方法,和步骤3中做的正好相反。
- 将发送方类型的实例对象以参数形式传到接收方类型的实例构造器中(或者在接收方类型的实例构造器中new一个发送方类型的实例,实际上WinForm应用中就是这么做的,在Form构造器中new一个button的实例对象),然后将接收方类型中的回调函数登记到发送方的事件成员上。
好了,基础知识到此结束,不懂的去看书本哈!下面让我们回到正题上,来看看在WinForm窗体应用中,当我们单击窗体上的一个Button之后究竟发生了什么。
在实际开发窗体应用时,我们一般都是直接将一个Button拖到Form上,然后Visual Studio会为我们新建一个Button实例对象,并设置它的样式。当我们点击按钮之后,会为我们生成如下的代码:
private void button1_Click_1(object sender, EventArgs e) { }
那我们就会想了,这部分代码属于接收方Form窗体对象的事件处理方法。那事件的发送方Button的负责引发(raises)事件的方法在哪里呢?在一开始用C#的事件时,我并没有对此深究,导致一直模模糊糊的,现在让我们一起看下Button的引发事件的方法究竟躲在哪里吧!
我们在button1_Click_1事件处理方法这里打一个breakpoint,然后运行程序并单击button1按钮。单步执行并查看程序的调用栈,如下图所示。
图1 单步执行程序并查看调用栈
我们看到,在button1_Click_1事件处理方法调用之前,调用了Control.OnClick方法,我们可以通过反射工具查看它的源代码,如下图所示:
图2 OnClick方法源码
从上图最后一行代码可以看出OnClick就是Button实际执行的引发(raises)事件的方法。也许你又有疑问了,base.Events[EventClick]是什么东东?其实,它是一个委托类型的list结构,可以容纳一个以上的委托,使用线性搜索算法来查找其中的条目,在委托条目较多时,查找效率会下降。EventClick是一个静态只读key值,通常赋以new Object(),用来索引事件成员(EventHandler类型)。好了,到这里,事件是不是就可以说懂了呢?不,我们还遗忘了一个点,那就是在声明事件成员时,声明成字段和属性有区别么?如果有区别,那区别大吗?下面让我们首先看一下声明成字段的情况。
图3 将MyEvent事件成员定义成字段形式。
图4 字段形式事件MyEvent的源代码
从图3和4可以看到,本质上来看,以字段形式定义的事件成员,编译器compile之后自动生成的add_MyEvent和remove_MyEvent两个方法是线程安全的,这意味着客户端程序每次向事件添加或删除一个委托时,必须先获得lock,这在非多线程开发的情形下加大了开销,毕竟获取和释放lock是一个耗费时间的过程。
图5 将MyCustomEvent事件成员定义成属性形式
图6 属性形式事件MyCustomEvent的源代码
从图5和6可以看到,以属性方式声明的事件成员,由于我们事先就定义好了add和remove方法的实现(其实这里就是所谓的“显式实现事件”,这样我们就能控制add和remove方法处理回调委托的方式),在编译之后,仅仅是向一个list中加入和删除委托,并没有考虑多线程的情形,因此,在非多线程的开发中效率较高。注意,事件属性的定义语法和一般的get或set访问器属性不同,事件属性定义中使用add和remove方法。
实际上,我们在Form窗体上拖放一个Button按钮控件时,就是用的属性形式的事件成员定义。如下图7和8所示。
图7 设计器自动生成的绑定Click事件代码
图8 Click事件成员的源代码形式
以上就是关于点击一个button发生的事情的真相了,客官,您弄懂了么?