事件

来源:互联网 发布:近几年的网络暴力事件 编辑:程序博客网 时间:2024/06/11 23:43

事件

在编程的世界里,我们经常面对类似的情形:要执行某项操作,但无法提前知道应该调用哪个方法或对象去执行。经典的例子是用来处理按钮被按下,菜单中某个选项被选择或其他的“事件”方法。

事件,在事件驱动的编程(event-driven programming)系统( 如微软的windows操作系统 )中,是某项经常发生的事情,它可以是由用户的操作引起的,也可以是系统状态的改变引起的,或者是由来自系统外(如同通过互联网)的消息触发的。

你一定可以想象,设计按钮(或下拉菜单或其他控件)的人未必会是使用这个控件的程序员。控件的设计者知道当按钮被按下的时候,使用按钮的程序员会希望做一些操作,但设计者无法知道是那些操作!

上面这个问题的解决方法是,按钮的创建者可说:“我的按钮发布了一系列事件,如点击。我的下拉菜单有另一些事件,如改变选择、增加选项,等等。想使用我的控件的程序员,可以在使用我控件的时候,将任何你希望和这些事件对应起来的方法,与这些事件关联起来。”

委托是包含方法的地址对象。这样的定义使他们可以有很多用途,但最理想的是如下两种:

“当某个事件发生时调用某个方法”(事件处理)

“当完成某项工作的时候调用某个方法”(回调)

在C#中,委托时语言的正常成员,它们被用来将事件和将用来处理事件的方法联系起来。实际上,事件是被限定了的委托。

 

间接调用

简单地说,委托实现了声明委托的类核使用委托的类之间的解耦:就是说,按钮控件的创建者并不需要知道每一个在页面上放置按钮的程序是如果使用按钮控件。

发布和订阅/观察者(Publish and Subscribe/Observer)

一个在编程中非常常见的“设计模式”就是控件(比如一个按钮)的创建者“发布”所有按钮会响应的事件(如点击),使用这个按钮的程序员(比如将按钮放在它们的表单上)会有选择地"订阅"一个或多个按钮的响应的事件。这样,如果你正在实现一个使用按钮控件的网页表单,你可以订阅(被通知)有人点击按钮这个事件,但不订阅鼠标悬停在按钮上这个事件。

与此非常相像的设计模式是观察者模式,以观察者模式来说,表单被称为观察者,而按钮被叫做被观察者。

在任何一种情况下,发布的机制是创建一个委托。而订阅的机制是定义一个能匹配委托方法签名和返回类型的方法。订阅类的方法一般被称为事件处理方法(event handler),因为处理了发布类所发生的事件。也就是,表单会处理点击按钮的事件。

按约定,.NET框架中的事件处理方法返回void值,有两个参数。一个参数是事件的“来源”(也就是发布类)。另一个是EventArgs派生而来的对象。推荐你的事件处理方法也遵守这种设计规范。

 EventArgs是所用事件数据的基类。EventArgs类将从Object继承除构造方法之外的所有方法,虽然它要添加一个公共静态字段empty,表示没有状态的事件(这样可以有效地使用这种事件)。EvnetArgs派生类包含了事件的所有信息。

 

    //一个存放事件信息的类    //这里它仅存放Clock类中的信息,    //但也可以存放其他状态信息    public  class TimeInfoEventArgs:EventArgs    {        public TimeInfoEventArgs(int hour, int minute, int second)        {            this.Hour = hour;            this.Minute = minute;            this.Second = second;        }        public readonly int Hour;        public readonly int Minute;        public readonly int Second;    }
//中心类:其他类要订阅/观察的类    //此类发布一个委托    //OnSecondChange    public class Clock    {        private int hour;        private int minute;        private int second;        //订阅者必须实现的委托        public delegate void SecondChangeHandler(object clock, TimeInfoEventArgs timeInformation);        //委托实例        public SecondChangeHandler SecondChange;        protected virtual void OnSecondChanged(TimeInfoEventArgs e)        {            if (SecondChange != null)            {                SecondChange(this, e);            }        }        //设置时钟运行        //将每一秒都引发一个事件        public void Run()        {            for (; ; )            {                 //休眠10毫秒                System.Threading.Thread.Sleep(10);                //获取当前时间                System.DateTime dt = System.DateTime.Now;                //如秒钟改变,通知订阅者                if (dt.Second != second)                {                    //创建TimeInfoEventArgs对象,传给订阅者                    TimeInfoEventArgs timeInformation = new TimeInfoEventArgs(                        dt.Hour, dt.Minute, dt.Second);                    OnSecondChanged(timeInformation);                }                //更新状态                this.second = dt.Second;                this.minute = dt.Minute;                this.hour = dt.Hour;            }        }    }
  //一个订阅者。DisplayClock订阅    //时钟事件。DisplayClock的任务是显示当前时间    public  class DisplayClock    {        //对于给定的时钟,订阅其SecondChangeHandler事件        public void Subscribe(Clock theClock)        {            theClock.SecondChange += new Clock.SecondChangeHandler(TimeHasChanged);        }        //实现委托功能的方法        public void TimeHasChanged(object theClock, TimeInfoEventArgs ti)        {            Console.WriteLine("Current Time:{0}:{1}:{2}", ti.Hour.ToString(),                ti.Minute.ToString(), ti.Second.ToString());        }    }
    //另一个订阅者,其任务是写入文件    public class LogCurrentTime    {        public void Subscribe(Clock theClock)        {            theClock.SecondChange += new Clock.SecondChangeHandler(WriteLogEntry);        }        public void WriteLogEntry(object theClock, TimeInfoEventArgs ti)        {            Console.WriteLine("Logging to file: {0}:{1}:{2}",                ti.Hour.ToString(), ti.Minute.ToString(), ti.Second.ToString());        }    }

 

  public class Program    {        static void Main(string[] args)        {            //创建一个时钟            Clock theClock = new Clock();            //创建显示时钟对象,让它订阅刚刚创建的时钟            DisplayClock dc = new DisplayClock();            dc.Subscribe(theClock);            //创建Log对象,让它订阅时钟            LogCurrentTime lct = new LogCurrentTime();            lct.Subscribe(theClock);            //让时钟开始运行            theClock.Run();            /*Console.WriteLine("Calling the method directly!");            System.DateTime dt = System.DateTime.Now.AddHours(2);            TimeInfoEventArgs timeInfomation = new TimeInfoEventArgs(                dt.Hour, dt.Minute, dt.Second);            theClock.SecondChange(theClock, timeInfomation);            Console.ReadKey();*/        }    }}



 委托与事件要点总结:

1、委托是一个类型安全的。

2、一个委托对象可以搭载多个方法。
3、一个委托对象搭载的方法并不需要属于同一个类,但所搭载的方法必须具有相同的原形和形式。
4、委托的实例将代表一个具体的函数

5、在任何一种情况下,发布的机制是创建一个委托。而订阅的机制是定义一个能匹配委托方法签名和返回类型的方法。订阅类的方法一般被称为事件处理方法(event handler),因为处理了发布类所发生的事件。也就是,表单会处理点击按钮的事件。通常的双击按钮产生的Click方法都是所谓的事件处理方法。

6、按约定,.NET框架中的事件处理方法返回void值,有两个参数。一个参数是事件的“来源”(也就是发布类)。另一个是EventArgs派生而来的对象。推荐你的事件处理方法也遵守这种设计规范。