C#高级特性及项目实战Day5
一、事件的本质
事件是软件系统里的两个子系统之间,或者两个模块之间,或者两个对象之间发送消息,并处理消息的过程。在面向对象的世界里,就可以统一认为是两个对象之间的行为。
两个对象之间发送的这种消息,对发送方来讲是产生一个事件,对接受方来讲是需要处理某个事件。这种消息可以是用户操作产生的或者软件系统里的某个对象产生的。
对象之间的事件处理
从上图可见,对象一产生一个事件,这个事件发生以后需要对象二执行某种动作。这就是事件机制。对象一是事件的产生者,或者发送者;对象二是事件的接收者或者订阅者。对象一产生某种消息,需要对象二响应并处理这给消息,这就是事件的本质。
以往的很多软件系统都在采用事件机制处理很多问题。例如从最本质的计算机体系中的软中断处理,到masm中的jump,到c/c++中的回调函数等等。只不过越高级的软件系统处理事件或者其提供的很多处理方法越接近人的思维,而越远离机器思维。构建软件系统的方法从本质上就是从机器思维走向人的思维的过程。
2、回调机制
如果按照c#的委托思想,B需要事先提供对事件处理函数的某些回调指针。这样,其它对象,例如A和C就去修改它的回调指针,把自己的方法联系到上面。但是它们之间的耦合关系就比上面简单了。
回调机制
回调机制的思想已经比较接近委托的概念。其实委托在本质上也就和回调指针差不多,只是概念上更加高级。对象B作为事件的发布者,事先定义一些回调函数指针,然后在本地合适的地方调用这些指针指向的函数。而事件订阅者或者处理者A和C所作的就是让给这些空指针赋值,把自己的事件处理方法赋给它,从而实现B调用A和C的方法。
在 C 或 C++ 中与委托最为相似的是函数指针。然而,函数指针只能引用静态函数,而委托可以引用静态方法和实例方法。当委托引用实例方法时,委托不仅存储对方法入口点的引用,还存储对为其调用该方法的类实例的引用。与函数指针不同,委托是面向对象、类型安全并且安全的。
三、事件机制的实现
1、委托的局限
如果单纯用委托,对于事件的发布者B来说,假设它发布事件e,对于事件e,它目前已经知道有A和C对象需要订阅这个事件。所以,它就申明两个委托对象引用(本质上类似于函数指针),然后让A和C对象来采用类似回调的机制订阅和响应事件。
如果后来,有个对象D也需要订阅B的事件e,它怎么办呢?一种情况是D修改B的一个委托对象引用,把自己的处理方法包装成一个委托对象付给它。这样,D就抢夺了A或者C的订阅。否则,就需要修改B的代码,添加一个类似的委托对象引用,以便让D来使用。
这样做的后果是事件发布者B需要申明很多委托对象的引用变量。结果是弄得代码维护比较混乱,并且使用者也很多,依赖关系也不容易搞清楚,容易发生错误。
2、事件的引入
有了委托,就提供了类似回调一样的功能。但是,回调机制需要事件发布者和事件订阅者双方的共同参与和努力。也就是,每增加一个订阅者,那么发布者对象就需要提供一个委托引用,让订阅者挂钩。
如果事件的发布者发布一个事件以后就不在关心谁来订阅它,那么以后的处理就交给了使用者,而发布者不再关心事件处理者的问题。
订阅机制
C#事件的事件就是这种订阅机制,真正的订阅。发布者不需要关心订阅者。
C#事件给订阅者提供了对事件响应的注册和反注册功能。订阅和撤销完全是事件接受方的行为。
C#事件机制的实现包括以下几步:
1、 事件发布者定义一个委托类型;
2、 事件发布者定义一个事件,并且关联到已经定义的委托上。
3、 事件订阅者需要产生一个委托实例,并把它添加到委托列表。
所以,事件event可以看成是一个事件列表,订阅者可以注册和撤销自己的响应和处理机制,但是它没有办法更改整个列表(原则上)。所以,提供了更强、更安全的方式。
四、事件机制的代码实例
应用程序结构图