一、事件的本质

事件是软件系统里的两个子系统之间,或者两个模块之间,或者两个对象之间发送消息,并处理消息的过程。在面向对象的世界里,就可以统一认为是两个对象之间的行为。

两个对象之间发送的这种消息,对发送方来讲是产生一个事件,对接受方来讲是需要处理某个事件。这种消息可以是用户操作产生的或者软件系统里的某个对象产生的。

对象之间的事件处理

从上图可见,对象一产生一个事件,这个事件发生以后需要对象二执行某种动作。这就是事件机制。对象一是事件的产生者,或者发送者;对象二是事件的接收者或者订阅者。对象一产生某种消息,需要对象二响应并处理这给消息,这就是事件的本质。

以往的很多软件系统都在采用事件机制处理很多问题。例如从最本质的计算机体系中的软中断处理,到masm中的jump,到c/c++中的回调函数等等。只不过越高级的软件系统处理事件或者其提供的很多处理方法越接近人的思维,而越远离机器思维。构建软件系统的方法从本质上就是从机器思维走向人的思维的过程。

二、事件机制的好处

1、直接调用

采用事件机制有什么好处?事件发送者为什么不直接调用事件接受者提供的处理函数呢?

调用机制

如果所示,两个对象之间的调用机制。对象B调用对象A的方法,可以通过函数指针或者跳转(汇编语言)等实现。这种方法造成的结果是A和B的紧密耦合,即B对A有很强的依赖性。可以看成B是事件的发布者,A是事件的响应和处理者。不过这种机制用事件机制解释从理论上就比较牵强了。同一种事物,其实现的思想不一样。

现在假设有个对象C也要响应B的事件。那么,按照上面的这种机制,需求修改对象B的代码,调用对象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可以看成是一个事件列表,订阅者可以注册和撤销自己的响应和处理机制,但是它没有办法更改整个列表(原则上)。所以,提供了更强、更安全的方式。

四、事件机制的代码实例

应用程序结构图

如图所示,事件发布对象发布一个事件;事件订阅对象订阅和处理该事件。

using System;

namespace EventExample

{

///<summary>

/// MainClass : 主应用程序类

///</summary>

class MainClass

{

///<summary>

///应用程序的主入口点。

///</summary>

[STAThread]

static void Main(string[] args)

{

EventPublisher publisher = new EventPublisher();

EventReader1 reader1 = new EventReader1(publisher);

EventReader2 reader2 = new EventReader2(publisher);

publisher.DoSomthing();

Console.WriteLine("This program already finished!");

Console.ReadLine();

}

}

///<summary>

/// EventPublisher : 事件的发布者。

///</summary>

public class EventPublisher

{

// 第一步是申明委托

public delegate int sampleEventDelegate(string messageInfo);

// 第二步是申明与上述委托相关的事件

public event sampleEventDelegate sampleEvent;

public EventPublisher()

{

}

public void DoSomthing()

{

/* ... */

// 激发事件

if(this.sampleEvent != null)

{

this.sampleEvent("hello world!");

}

/* ... */

}

}

///<summary>

/// EventReader1 : 事件的订阅者1。

///</summary>

public class EventReader1

{

public EventReader1(EventPublisher publisher)

{

publisher.sampleEvent +=

new EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);

}

private int ResponseEvent(string msg)

{

Console.WriteLine(msg + " --- This is from reader1");

return 0;

}

}

///<summary>

/// EventReader2 : 事件的订阅者2。

///</summary>

public class EventReader2

{

public EventReader2(EventPublisher publisher)

{

publisher.sampleEvent +=

new EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);

publisher.sampleEvent +=

new EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);

}

private int ResponseEvent(string msg)

{

Console.WriteLine(msg + " --- This is from reader2");

Console.WriteLine("Please:down enter key!");

Console.ReadLine();

Console.WriteLine("ok");

return 0;

}

}

}

程序运行结果

总结:事件发布者发布的事件在实质上可以看成对外提供的回调函数指针列表。这个列表的容量可以动态增长。事件订阅者可以把自己的事件注册到这个列表或者撤销注册,但是它从原则上无法更改或者对其它订阅者的注册产生影响。事件发布者通过两种手段使得订阅者正确地使用事件机制:一是定义一种delegate委托类型,事件订阅者只能按照这种类型定义事件的处理方法;二是定义与这个委托相关的event对象,使得订阅者只负责注册和撤销自己的处理过程而不能随意对别人的处理过程产生影响。

从运行结果和reader2对象把同一个处理方法注册了两次的前提可以看到,对于一个事件,同一个订阅者可以把同一个处理过程注册多次,而这个方法最终也会被执行多次。

执行事件订阅列表中方法的顺序不能被保证;而且,在这里采用的是同步调用方法,只有一个响应函数执行完毕,其它函数才会被执行。如果要方法不被阻塞(包括这里的等待用户输入等),就需要采用异步调用方式。

C#委托和事件的概念相关推荐

  1. C#编程利器之四:委托与事件(Delegate and event) (上)

    本文试图在.net Framework环境下,使用C#语言来描述委托.事件的概貌.希望本文能有助于大家理解委托.事件的概念,理解委托.事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处. ...

  2. 庖丁解牛——深入解析委托和事件

    这篇博文我不讲委托和事件的概念,因为大段的文字概念没有任何意义. 具体想了解,委托和事件的概念可以MSDN查阅. 我这篇文章的主题思路是委托如何一步步进化成事件: 何为委托--->委托来实现事件 ...

  3. CSharp(C#)语言_委托和事件区别详解

    委托和事件区别详解 委托和事件的概念 委托 事件 委托和事件的作用 委托 事件 委托和事件的区别 委托和事件的详细解答请看C#系列文章 委托和事件代码实践 委托 事件 总结 委托和事件的概念 委托   ...

  4. C# 概念 委托和事件

    源码下载:http://www.tracefact.net/SourceCode/Delegates-and-Events-in-CSharp.rar C# 中的委托和事件 引言 委托 和 事件在 . ...

  5. [C#]委托和事件(讲解的非常不错)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  6. 大白话系列之C#委托与事件讲解(一)

    从序言中,大家应该对委托和事件的重要性有点了解了吧,虽然说我们现在还是能模糊,但是从我的大白话系列中,我会把这些概念说的通俗易懂的.首先,我们还是先说说委托吧,从字面上理解,只要是中国人应该都知道这个 ...

  7. 对C#下函数,委托,事件的一点理解!

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 今天一来是有点 ...

  8. C#综合揭秘——深入分析委托与事件

    引言 本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单. 还将为您解释委托的协变与逆变,以及如何使用 Deleg ...

  9. C# 关于委托和事件的妙文:通过一个例子详细介绍委托和事件的作用;Observer模式简介...

    委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见 ...

最新文章

  1. Auto Machine Learning 自动化机器学习笔记
  2. php调用dll函数,[转载]matlab调用DLL中的函数
  3. Docker容器学习
  4. BGP策略路由的实现
  5. 安卓手机5个好用的思维导图软件
  6. 【转载】从创业者角度看《印度合伙人 Padman》后的一点感受
  7. 扫描全能王?原来图像技术可以这样子玩
  8. django建议入门-FYI
  9. 【无限互联】iOS开发视频教程— 2.8 iPhone开发之swtch语句
  10. 几个连接虚拟机和云服务器的软件
  11. 预测分析·民宿价格预测【和鲸社区】
  12. Win7怎么共享无线 Win7无线网络共享全攻略
  13. ps-色彩模式与图像色彩调整
  14. S3C6410裸机SD卡驱动(SDIO模式)
  15. 51单片机课设——模拟电梯控制系统
  16. 公司突然断网故障排查
  17. VTM10.0代码学习3:DecSlice_decompressSlice()
  18. 单词测试通关学英语的软件,学习软件有哪些,给大家推荐一款靠谱的 | 外教英评网...
  19. 踩雷高德API省市区乡镇街道地址保存报错
  20. PMP计算题公式知识点整理

热门文章

  1. 二维码的生成加背景图片的嵌套-支付宝(Java)
  2. Eclipse启动报错:org.eclipse.e4.core.di.InjectionException: java.lang.NoClassDefFoundError: javax/annotat
  3. 查询英文期刊缩写的网站
  4. 网页中无法添加微信好友怎么办?如何一键唤起微信添加好友?
  5. 哪些IC设计公司会在CMMB竞争中突围?
  6. 双系统启动项设置为上次所选系统
  7. 【操作系统】某寺庙,住着一个老和尚和若干小和尚,有一个水缸,由小和尚提水入缸供老和尚饮用。水缸可以容纳10桶水,水取自同一口井中,由于水井口窄,每次只能容纳一个水桶取水,水桶总数为3个。每次往水缸中倒
  8. SonarQube在Windows环境下下载安装,中文包下载安装,mysql配置,maven配置,idea配置,项目配置
  9. oracle裁员原因_甲骨文中国裁员 部分员工不满补偿方案
  10. dropbox免费容量_2020年免费云存储指南:Google Drive VS Dropbox VS iCloud VS OneDrive VS Amazon