zoukankan      html  css  js  c++  java
  • 一点关于事件的讨论

    window两大机制:基于事件驱动的消息机制,绘图机制(补充:消息由事件而来,不同事件产生不同消息,也可以认为事件与消息之间存在一对一的映射关系)

    ————————————————————————————

    事件机制在多种语言中都有体现:

    比如C# :+=,-=(补充,C#中事件的定义基于委托,并且只能触发与委托具有相同签名的方法)

    比如dojo:dojo.connect,dojo.disconnect

    比如javascript:element.attachEvent,element.detachEvent,element.addEventListener,element.removeEventLister

    比如flex:

    不同技术之间有相似,但是也有区别,想提取普适性的原理,需要足够抽象,需要数学的帮助

    ————————————————————————————

    C#事件的作用:

    1、类A通知类B当事件d发生的时候,执行方法g

    (补充:类A和类B可以是同一个类,也可以是不同的类;方法g不一定属于类A,方法g也不一定属于类B,方法g可能属于第三方类;方法g可以是带参函数,也可以是无参函数)

    2、如果方法g带参数,那么就可以传递参数,参数一般有两个,一为:事件的触发者sender,二为:事件参数e

    ————————————————————————————

    C#事件分类:

    1、系统事件:button.click

    2、自定义事件:taskstartevent,taskdoingevent,taskfinishedenvent

    ————————————————————————————

    C#事件的触发:

    1、系统触发:当click button的时触发button.click

    2、自定义触发: 当执行ontaskstart,ontaskdoing,ontaskfinished方法时分别触发taskstartevent,taskdoingevent,taskfinishedenvent

    ————————————————————————————

    引用三篇文章的部分内容:

    *********************

    方法三:事件回调

     public delegate void MyDelegate(string text);   

     public partial class Form2 :Form1    

     {          

           //定义该委托的事件        

           public event MyDelegate MyEvent;        

           public Form2(string text)        

           {             

               InitializeComponent();              

               this.textBox1.Text = text;        

           }        

           private void btnChange_Click(object sender, EventArgs e)        

          {              

               //触发事件,并将修改后的文本回传            

               MyEvent(this.textBox1.Text);            

               this.Close();        

          }    

     }    

    Form1:C#代码  

    public partial class Form1 :Form      

    {          

          public int index = 0;          

          public string text = null;          

          public Form1()          

         {            

             InitializeComponent();          

         }            

         private void listBox1_SelectedIndexChanged(object sender, EventArgse)          

         {            

             if (this.listBox1.SelectedItem != null)            

             {                

                 text = this.listBox1.SelectedItem.ToString();                

                 index = this.listBox1.SelectedIndex;                

                 Form2 form2 = new Form2(text);                  

                 //注册form2_MyEvent方法的MyEvent事件                  

                 form2.MyEvent += new MyDelegate(form2_MyEvent);                  

                form2.Show();              

             }          

          }          

       //处理

       void form2_MyEvent(string text)        

        {            

          this.listBox1.Items.RemoveAt(index);            

          this.listBox1.Items.Insert(index, text);          

        }      

    }   

    ******************************

          事件(event)是一个非常重要的概念,我们的程序时刻都在触发和接收着各种事件:鼠标点击事件,键盘事件,以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮,某个文件发生了改变,socket上有数据到达。触发事件的对象称作发送者(sender),捕获事件并且做出响应的对象称作接收者(receiver),一个事件可以存在多个接受者。

          在异步机制中,事件是线程之间进行通信的一个非常常用的方式。比如:用户在界面上按下一个按钮,执行某项耗时的任务。程序此时启动一个线程来处理这个任务,用户界面上显示一个进度条指示用户任务执行的状态。这个功能就可以使用事件来进行处理。可以将处理任务的类作为消息的发送者,任务开始时,发出 “TaskStart”事件,任务进行中的不同时刻发出“TaskDoing”事件,并且携带参数说明任务进行的比例,任务结束的时候发出 “TaskDone”事件,在画面中接收并且处理这些事件。这样实现了功能,并且界面和后台执行任务的模块耦合程度也是最低的。

    *****************************

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

    一、事件的本质

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

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

    对象之间的事件处理 

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

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

    二、事件机制的好处

    1、直接调用

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

    调用机制

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

    现在假设有个对象C也要响应B的事件。那么,按照上面的这种机制,需求修改对象B的代码,调用对象C的方法。这样机制造成了非常强的依赖关系。代码的修改和扩展非常麻烦。如果对象越多,这种关系越多,整个系统越复杂。如果一个系统里面对象很多,这种依赖关系也很多的情况下,这种调用关系就会十分复杂,对系统的健壮性和优良性会造成影响。

    2、回调机制

    如果按照c#的委托思想,B需要事先提供对事件处理函数的某些回调指针。这样,其它对象,例如AC就去修改它的回调指针,把自己的方法联系到上面。但是它们之间的耦合关系就比上面简单了。

    回调机制 

    回调机制的思想已经比较接近委托的概念。其实委托在本质上也就和回调指针差不多,只是概念上更加高级。对象B作为事件的发布者,事先定义一些回调函数指针,然后在本地合适的地方调用这些指针指向的函数。而事件订阅者或者处理者AC所作的就是让给这些空指针赋值,把自己的事件处理方法赋给它,从而实现B调用AC的方法。

     C  C++ 中与委托最为相似的是函数指针。然而,函数指针只能引用静态函数,而委托可以引用静态方法和实例方法。当委托引用实例方法时,委托不仅存储对方法入口点的引用,还存储对为其调用该方法的类实例的引用。与函数指针不同,委托是面向对象、类型安全并且安全的。

    三、事件机制的实现

    1、委托的局限

    如果单纯用委托,对于事件的发布者B来说,假设它发布事件e,对于事件e,它目前已经知道有AC对象需要订阅这个事件。所以,它就申明两个委托对象引用(本质上类似于函数指针),然后让AC对象来采用类似回调的机制订阅和响应事件。

    如果后来,有个对象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++爱问的面试问题
    Ognl底层使用
    [勘探开发]成绩,全栈开发,健全&amp;借贷
    FMS4
    Flex远程调用机制RemoteObject应用技巧
    Flex开发框架cairngorm入门实例
    RC1意思
    获取JAVA[WEB]项目相关路径的几种方法
    排序算法
    jQuery Validate
  • 原文地址:https://www.cnblogs.com/zhangjun1130/p/1995227.html
Copyright © 2011-2022 走看看