C#事件和c#委托有什么关系?

事件的定义和使用方式与委托极其类似,那么二者又是何关系呢?

经常听人说,委托的本质是一个类型,而事件的本质是一个特殊的委托类型的实例。关于这个解释,最好的办法莫过于通过查看原代码和编译后的IL代码进行分析。.

① 回顾刚刚的代码,在ConsoleManager类中定义了一个事件成员

public event EventHandler<ConsoleEventArgs> ConsoleEvent;

EventHandler 是.NET框架中提供的一种标准的事件模式,它是一个特殊的泛型委托类型,通过查看元数据可以验证这一点:

[Serializable]
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
正如上面代码所示,我们定义一个事件时,实际上是定义了一个特定的委托成员实例。该委托没有返回值,并且有两个参数:一个事件源和一个事件参数。而当事件的使用者订阅该事件时,其本质就是将事件的处理方法加入到委托链之中

② 下面通过Reflector来查看一下事件ConsoleEvent的IL代码(中间代码),可以更方便地看到这一点:

首先,查看EventHandler的IL代码,可以看到在C#编译器编译delegate代码时,编译后是成为了一个class。

C#事件和c#委托有什么关系?

其次,当C#编译器编译event代码时,会首先为类型添加一个EventHandler<T>的委托实例对象,然后为其增加一对add/remove方法用来实现从委托链中添加和移除方法的功能。

C#事件和c#委托有什么关系?

通过查看add_ConsoleEvent的IL代码,可以清楚地看到订阅事件的本质是调用Delegate的Combine方法将事件处理方法绑定到委托链中

L_0000: ldarg.0 
L_0001: ldfld class [mscorlib]System.EventHandler`1<class ConsoleEventDemo.ConsoleEventArgs> ConsoleEventDemo.ConsoleManager::ConsoleEvent
L_0006: stloc.0 
L_0007: ldloc.0 
L_0008: stloc.1 
L_0009: ldloc.1 
L_000a: ldarg.1 
L_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
L_0010: castclass [mscorlib]System.EventHandler`1<class ConsoleEventDemo.ConsoleEventArgs>
L_0015: stloc.2 
L_0016: ldarg.0 
L_0017: ldflda class [mscorlib]System.EventHandler`1<class ConsoleEventDemo.ConsoleEventArgs> ConsoleEventDemo.ConsoleManager::ConsoleEvent

总结:事件是一个特殊的委托实例,提供了两个供订阅事件和取消订阅的方法:add_event 和 remove_event,其本质都是基于委托链来实现