一个简单的事件的例子:“单击一个按钮后保存一个文件”
在这个例子中事件是单击按钮,事件处理操作是保存文件。
在C#中创建事件很简单
public event EventHandler Click; //声明事件
private void button1_Click(object sender, EventArgs e){} //声明事件处理程序
this.button1.Click += new System.EventHandler(this.button1_Click); //注册事件处理程序
如果你写过WinForm程序,那对上面的代码一定不陌生。
然而就是C#提供的这种字段风格的事件(field-like event)的简写混淆了委托和事件。
看起来事件Click就是一个保存了对事件处理方法引用的一个委托实例。
但是事实并不是这样:事件不是委托实例–只是成对的add/remove方法。
想一想,当我们在使用属性的时候,感觉就像是直接在对它的字段进行取值和赋值,但实际上是在调用方法,也就是取值方法和赋值方法。实现属性时,可以在那些方法中做任何事情。但凑巧的是大多数属性都是实现了简单的字段,有时会在赋值方法中添加一些校验机制,有时则会添加一些线程安全性。
事件的存在的首要理由和属性差不多—它们添加了一个封装层,实现发布/订阅模式。
当我们在订阅或取消一个事件时,看起来就像是在通过+=和-=运算符使用委托类型的字段,但和属性一样,这个过程实际是在调用方法(add和remove方法)。
下面我们来看一个例子:
namespace EventConsoleApp
{
class EventDemo
{
public event EventHandler MyEvent;
}
class Program
{
public int MyProperty { get; set; }
static void Main(string[] args)
{
}
}
}
用IL DASM查看一下编译器生成的IL代码:
先看我们熟悉的属性,在编译成IL代码时,生成了一个私有字段k__BackingField和两个方法get_MyProperty和set_MyProperty,这样就是实现了字段的封装。
再来看看事件,我们使用event关键字在类中定义事件成员,当编译器在类中遇到event关键字时,它会创建一个私有成员,图中MyEvent,还创建了两个公开方法,即add_MyEvent和remove_MyEvent,这些方法是事件挂钩,他们允许委托和事件委托合并或从该事件移除,这些详细信息对程序员是隐藏的。
这时候,是不是觉得事件更像属性了呢?