对象间通信机制-事件、回调等

来源:互联网 发布:一键安装php集成环境 编辑:程序博客网 时间:2024/06/01 07:40

早期程序使用输入-操作-输出的机制,整个流程完全由程序员事先设定好。

事件驱动机制是指程序按照事件发生的次序随机执行而不是按照编程时就定义好的顺序执行当某个事件发生时程序将找到相应的事件处理程序来处理事件。所以具有顺序结构的编程显然不具有事件驱动的先决条件。
面向对象程序设计当中采用的就是事件驱动机制。比如说鼠标左击、双击都是具体事件,根据这些事件启用预先设置的相应动作就是事件驱动机制。注意这些事件是随机触发的,并没有预先决定它们的发生顺序,预先设计好的是它们的处理方法(事件handler)

当前,事件机制是组件和交互式开发所必须的一种技术。

事件机制例子:当用户输入一个字符或者是单击一次鼠标,一个事件就发生了。任何一个程序中的对象都可能被通知到有某事件发生,只要它实现了一些相应的接口或者是事先在一个具备事件监听功能的对象那里注册好了,告诉它“有什么事件时通知我一下”。被通知后,对象可以做出不同的响应,以实现各自的功能。

事件触发机制相对于简单的轮询机制来说,优点如下:

图形用户界面就是利用这种事件响应来实现接收用户操作的。那么到底是怎么实现的呢。当前各种语言、类库、平台大都会提供事件机制,至于实现方式便有很多种。

C# 委托方式

C#的委托事件机制非常优雅的解决了对象间通信机制。

首先可以将委托delegate看成是函数指针类型的抽象:

public delegate void delegatesome(type1 para1, type2 para2, type3 para3, ...);
//EventHandler是一种委托,看成是函数指针类型

事件event是一种带特殊签名的委托的实例,且其原型声明必须是如下形式:

public delegate void EventHandler(object sender ,MyEventArgs e) ; 

事件定义:

public event EventHandler ActionEvent;

控件类(或其他可以触发事件的类):

class Control
{
    public event EventHandler SomeEvent;//声明可以触发的事件
    //...
    protected OnSomeEvent(EventArgs e)   //触发事件
    {
        if(SomeEvent)//判断是否有注册处理函数
        {
            SomeEvent(this, e);//这里才是触发事件的实质部分!!
        }
     }
}

因为一般控件的触发都是由操作系统检测然后自动调用protected的成员OnSomeEvent(EventArg e)函数,既然是protected,意味着我没有办法直接通过程序模拟触发事件的发生。当然你可以自定义事件,然后决定何时触发。

下面是处理类:

class Consumer
{
    private Control ctl0 = new Control();
 
    //...
    public Consumer()
    {
        //..
        this.ctl0.SomeEvent += new System.EventHandler(this.ctl0_SomeEvent);
    }
 
    private ctl0_SomeEvent(object sender, EventArgs e)
    {
        //do things
    }
Win32/MFC  消息发送与回调函数机制

消息驱动机制 
1、消息驱动与消息循环 
“消息”是windows运行机制中一个基本而又重要的概念。消息是一个报告事件发生的通知,消息驱动是围绕消息的产生与处理展开的,并依靠消息循环机制来实现。 
从程序设计的观点看,某条消息可被视为某个事件的发生,比如点击鼠标。事件即可以由用户引发,也可以由应用程序产生,当然Windows本身也能发出消息。Windows应用程序的消息来源有4种:输入消息,控制消息,系统消息,用户消息。 
Windows是一个多任务操作系统,所以没有哪一个程序能够独占系统的资源,资源都是由Windows统一管理的。那么某个程序是如何获得用户的信息呢?事实上,Windows在时刻监视着用户的每个举动,并分析用户的动作与哪一个程序相关,然后将动作以消息的形式发送给当前的应用程序。相反,应用程序也在时时等着消息的到来,一旦发现它的消息队列中有未处理的信息,就获取并分析该消息,并根据消息所包含的内容采取适当的动作来响应。这里我们引出另一个概念“消息驱动”。比如当你单击file菜单的时候,首先这个动作被windows所捕获,而不是应用程序。经分析windows知道该动作该由哪个应用程序处理,然后windows就发送WM_COMMAND消息给该应用程序,它告诉应用程序,你单击了file菜单。应用程序得知这一消息后,便采取相应的动作来响应它,进行“消息处理”。Windows为每个线程维护了相应的消息队列,应用程序的任务就是不停地从特定的消息队列中获取消息、分析消息并处理消息,直到消息(WM_QUIT)为止。这个过程的程序结构称为“消息循环”。 
2、消息传送 
发送消息和寄送消息 
发送一个消息时,系统直接调用窗口进程。通信是即时的。直到窗口进程为调用函数返回一个结果后,应用程序才能继续。 
寄送一个消息时,系统把消息发送到拥有该窗口的应用程序消息队列中。消息队列是系统定义的一个内存块,用于临时存储消息,或是把消息直接直接发给窗口过程。每个窗口维护自己的消息队列,从中取出消息,利用窗口函数进行处理。一有空闲,应用程序就搜索消息队列,并在消息队列中处理消息,即从队列中删除他们。调用函数发送消息后就立即返回,但结果只是表示消息寄送成功与否,而不表示被调用窗口进程的结果。通常鼠标和键盘消息是寄送的。
3、消息处理 
Windows程序在处理消息时使用了“回掉函数”的特殊函数。这个函数由应用程序定义,但并不由应用程序来调用,而是共操作系统或者其子系统来调用的。这种调用通常在某一事件发生,或者在窗口或字体被枚举时发生。Windows向程序员所能发送的消息多达百种,但是,对于一般的应用程序来说,只是其中的一部分有意义。 
4、Windows对消息驱动机制的支持 
Windows操作系统包括3个内核基本模块: 
GDI:负责在屏幕上绘制象素、打印硬考贝输出,绘制用户界面 
KERNEL:支持与操作系统密切相关的功能。如进程加载,系统调用 
USER:为所有的用户界面对象提供支持,它用于接收和管理所有输入消息、系统消息,并把他们发给相应的窗口的消息队列。 
上述GDI、KERNEL和USESR模块中的库函数可被应用程序调用,也可被其他程序模块调用。Windows把包含库函数的模块称为EXPORT,在WINDOWS提供的一种新的EXE文件中有一个入口表用于指明模块内每个输出函数的地址。 
从应用程序方面,用到的库函数被认为是IMPORT函数。应用程序对一个入口函数发出的远程调用可用不同的重定位表来确定。几乎所有的应用程序都至少包含一个入口库函数或者称为被外部调用的函数。该windows库函数一般来自某个程序模块,用于从WINDOWS接收消息,该函数的使用标志必须是EXPORT,这才能使WINDOWS允许它被一个外部模块正常调用。


关于回调函数,事件模型,以及一些牢骚

首先,我也是经历了“认为回调函数比事件模型好”,“认为事件模型比回调函数好”,这两个阶段都走过才来说这样的话的。其实“回调函数”和“事件模型”可以作为架构方式的两个标志。这里,我就单单说说它们的优缺点吧。
 
回调函数
优点:
1.没有内存泄漏问题
2.书写简单,一个方法的回调函数数量和意义是固定的,不需要刻意查找需要的回调函数名称
3.你无法对同一个内容加上两个回调函数,可以避免重复添加的问题
4.回调函数的参数列表比较灵活,可以直接写,而且是固定的,写错了执行时会报错,方便排错
5.回调函数以及它的参数是固定的,多了少了都不行,如果对方和你不同可以马上知道,方便协作。
 
缺点
1.回调函数不能保持引用,可能函数还没有执行就被回收了,可靠性较低。
2.每次增加内容,都需要修改所有函数的参数列表。添加新的回调函数则更难。
3.你无法对同一个内容加上两个回调函数,功能受到很大的限制。
4.回调函数的参数列表除非看代码或者帮助(注释)是不知道的,使用较麻烦。
5.回调函数以及它的参数是固定的,多了少了都不行,对方必须跟着你脚步走,不利于并行开发。
 
事件模型
优点:
1.使用不当,会造成内存泄露问题。
2.事件可以方便地修改。添加新事件,修改数据类型,并在不同的地方使用。
3.你可能对同一个事件监听两个事件函数,而且事件可以进行传递等等操作,拥有许多便捷的特性(诸如显示对象的冒泡)
4.事件无论是事件类型还是事件传递的数据类型都可以在编译期间检测,而且拥有代码提示,容易使用。
5.事件很自由,即使没有接受方也可以发送,非常适合并行开发。
 
缺点:
1.只要使用强引用,函数最终都可以执行,可靠性高(当然某些特别的诸如SharedObject那是特殊情况就算了)
2.每个事件类型可以传递的数据类型是固定的,为适应新的情况增加新的事件会增加很多代码。而且还需要移除等等操作,设置属性也需要单独的代码。
3.你可能会不小心对同一个事件监听了两个事件函数,造成重复调用。而且可能会在不需要的时候触发事件。
4.事件类型多到了一定程度,反而会不知道选择哪个事件类型才能找到想发送的特定事件。所以不可能一种情况对应一个事件类型。但容易用Object来通用化的话,它原来的优势又没了。
5.事件即使没有接受方也可以发送,因此,你根本不知道对方是否成功接受到了事件。
 
将4部分的1,2,3,4,5对照起来应该能表达出我的意思了。
我没什么意思。也就是“看情况”罢了。当然,一定要强制只用一个的话,我选事件。
 
但简单地说谁就好谁就差的话,真的有认真考虑手边的事情吗?程序是不能空谈的,装B的话,则更不用说了。