转载

事件和委托(Event vs. Delegate)-事件阉割版的Delegate

引言

关于事件和委托的文章看过很多,但总是记不完整。尤其是在给别人讲解的时候总是很零碎,所以在此整理一下,也供大家参考,很请大家不吝赐教。

本文从一个子类如何触发基类的事件(Event),引出事件和委托的共性和区别。再简单分析下背后原因,深层原因也给大家提供了部分参考资料。欢迎大家留言讨论。

事件和委托(Event vs. Delegate)-事件阉割版的Delegate

问题

“如果我想在子类里触发父类的事件(Event)应该如何实现?”(可以先自己做下或者想下,再继续看您的做法是否也曾有我碰到技术误区)

问题分解为两步:

  1. 父类定义了一个事件(Event)
  2. 子类触发父类中定义的这个事件(Event)

最初的想法是:

class BaseClass {  public delegate void CompletedEventHandler();  public event CompletedEventHandler WorkCompleted;  private void DoSomeThingInBase()  {   //do something then to notify other class   if (WorkCompleted != null)   {    WorkCompleted();   }  } } class subClass : BaseClass {  private void DoSomeThingInSub()  {   //do something then to notify other class   if (WorkCompleted != null)   {    WorkCompleted();   }  } } 

您认为上面的代码会如愿以偿吗?

我这么一问大家应该就开始怀疑啦。没错,这种实现这停留在想法里实际上是行不通的,编译器会给出如下的错误提示:

The event 'BaseClass.WorkCompleted' can only appear on the left hand side of += or -= (except when used from within the type 'BaseClass')

问题的解决

我给出我的答案(希望大家也能给出更好的版本):

class BaseClass {  public delegate void CompletedEventHandler();  public event CompletedEventHandler WorkCompleted;  private void DoSomeThingInBase()  {   //do something then to notify other class   RaiseCompletedEvent();  }  protected void RaiseCompletedEvent()  {   if (WorkCompleted != null)   {    WorkCompleted();   }  } }  class subClass : BaseClass {  private void DoSomeThingInSub()  {   //do something then to notify other class   RaiseCompletedEvent();  } } 

问题解释

为什么会出现刚才的问题呢?

其实这个问题的换一个问法,您或许会直接答出来?那就是“事件和委托的区别和联系是什么?”

我也计划从这个角度来解释这个问题:

先一句话解释下编译器报错的原因

事件是一种特殊的委托,或者说是受限制的委托,是委托一种特殊应用,只能施加+=,-=操作符。二者本质上是一个东西。

问题挖掘

从委托说起:

如何定义委托?

delegate + function signature // delegate相当于class关键字, function name相当于定义的委托类型.  delegate void Action();

如何创建委托实例?

Action action;

事件是怎样定义和实例化的?

delegate void ActionHandler(object sender, EventArgs args);//先定义委托类型 event ActionHandler ActionEvent;//定义事件实例

对头,事件的定义和委托的定义如出一辙,这也再次从形式上证实了“事件和委托”的一致性。

但是,事件和委托还是有个重要的区别。

事件Vs. 委托

编译成创建一个私有的委托示例, 和施加在其上的add, remove方法.

为说明差异,在示例代码中加入了一个Delegate来类比,代码如下:

class BaseClass {  public delegate void CompletedEventHandler();  public event CompletedEventHandler WorkCompleted;  public CompletedEventHandler WorkCompletedDelegate;  private void DoSomeThingInBase()  {   //do something then to notify other class   RaiseCompletedEvent();   if (WorkCompletedDelegate != null)   {    WorkCompletedDelegate();   }  }  protected void RaiseCompletedEvent()  {   if (WorkCompleted != null)   {    WorkCompleted();   }  } } class SubClass : BaseClass {  private void DoSomeThingInSub()  {   //do something then to notify other class   RaiseCompletedEvent();   if (WorkCompletedDelegate != null)//不会像事件那样在编译时报错   {    WorkCompletedDelegate();   }  } } 

ILDASM 查看编译后的结构,可以看出事件被单独赋予了两个方法(实际上就是因为编译器已经对其进行了阉割,不让其他类直接Invoke事件,但delegate是健全的)

事件和委托(Event vs. Delegate)-事件阉割版的Delegate

他山之石

委托和事件支持静态方法和成员方法, delegate(void * pthis, f_ptr), 支持静态返方法时, pthis传null.支持成员方法时, pthis传被通知的对象.

委托对象里的三个重要字段是, pthis, f_ptr, pnext, 也就是被通知对象引用, 函数指针/地址, 委托链表的下一个委托节点.

源码下载

下载

总结

delegate关键字类比于class关键字, 定义的是一种委托类型,然后再创建委托实例.

创建委托实例时, 用event关键字来修饰就变成了创建一个事件. 也就是事件是一种特殊的委托.

委托常用来表达回调,事件表达外发的接口

(事件是一个阉割版的delegate,只能对其进行+=,-=的操作,不能出去偷情(被别人Invoke 他/她),不知我的理解是否准确,还请各位路过的大侠赐教)

参考

事件与委托的联系和区别 (文章简短但是内容实在)

C#:委托和自定义事件(基础内容)

C#综合揭秘——深入分析委托与事件 (内容颇多,涉及面较宽)

初识Ildasm.exe——IL反编译的实用工具

正文到此结束
Loading...