引言
关于事件和委托的文章看过很多,但总是记不完整。尤其是在给别人讲解的时候总是很零碎,所以在此整理一下,也供大家参考,很请大家不吝赐教。
本文从一个子类如何触发基类的事件(Event),引出事件和委托的共性和区别。再简单分析下背后原因,深层原因也给大家提供了部分参考资料。欢迎大家留言讨论。
问题
“如果我想在子类里触发父类的事件(Event)应该如何实现?”(可以先自己做下或者想下,再继续看您的做法是否也曾有我碰到技术误区)
问题分解为两步:
- 父类定义了一个事件(Event)
- 子类触发父类中定义的这个事件(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是健全的)
他山之石
委托和事件支持静态方法和成员方法, delegate(void * pthis, f_ptr), 支持静态返方法时, pthis传null.支持成员方法时, pthis传被通知的对象.
委托对象里的三个重要字段是, pthis, f_ptr, pnext, 也就是被通知对象引用, 函数指针/地址, 委托链表的下一个委托节点.
源码下载
总结
delegate关键字类比于class关键字, 定义的是一种委托类型,然后再创建委托实例.
创建委托实例时, 用event关键字来修饰就变成了创建一个事件. 也就是事件是一种特殊的委托.
委托常用来表达回调,事件表达外发的接口
(事件是一个阉割版的delegate,只能对其进行+=,-=的操作,不能出去偷情(被别人Invoke 他/她),不知我的理解是否准确,还请各位路过的大侠赐教)
参考
事件与委托的联系和区别(文章简短但是内容实在)
C#:委托和自定义事件(基础内容)