1 事件的基本概念
在基于Windows平台的程序设计中,事件(event)是一个很重要的概念。因为在几乎所有的Windows应用程序中,都会涉及大量的异步调用,比如响应点击按钮、处理Windows系统消息等,这些异步调用都需要通过事件的方式来完成。那么什么是事件呢?所谓事件,是对象内部状态发生了某些变化、或者对象做某些动作时(或做之前、做之后),向外界发出的通知。比如用户点击了鼠标、socket上有数据到达等。
事件发布者: 那个触发(raise)事件的对象称为事件的发布者(event publisher)
事件的订阅者: 捕获并响应事件的对象称为事件的订阅者(event Subscriber)
那么,当事件发布者发出的事件通知时,外部环境可能需要应对事件的发生,而做出相应的反应[场景一];又或者外部环境不做出任何反应[场景二],及灭有任何的事件订阅者.本文要探讨的是场景一的情况.因为从某种角度来说,场景二没有意义.
事件的衔接:事件的发送与接收必须通过某种介质而发生火花,两者必须挂钩后,事件才可能会被执行。否则,孤立的事件不会被执行。
C#事件就是基于windows消息处理机制的,只是封装的更好,让开发者无须知道底层的消息处理机制,就可以开发出强大的基于事件的应用程序来。那么事件编程又给我带来什么好处呢.
事件编程的优点:
在以往我们编写某类程序中,往往采用等待机制,为了等待某件事情的发生,需要不断地检测某些判断变量,而引入事件编程后,大大简化了这种过程:
- 使用事件,可以很方便地确定程序执行顺序和时间。
- 当事件驱动程序等待事件时,它不占用很多资源。事件驱动程序与过程式程序最大的不同就在于,程序不再不停地检查输入设备,而是呆着不动,等待消息的到来,每个输入的消息会被排进队列,等待程序处理它。如果没有消息在等待,则程序会把控制交回给操作系统,以运行其他程序。
- 事件简化了编程。操作系统只是简单地将消息传送给对象,由对象的事件驱动程序确定事件的处理方法。操作系统不必知道程序的内部工作机制,只是需要知道如何与对象进行对话,也就是如何传递消息。
事件具有以下特点:
发布者确定何时引发事件,订阅者确定执行何种操作来响应该事件。
一个事件可以有多个订阅者。一个订阅者可处理来自多个发布者的多个事件。
没有订阅的事件永远不会被调用。
事件通常用于通知用户操作,例如,图形用户界面中的按钮单击或菜单选择操作。
如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序。要异步调用事件,请参见使用异步方式调用同步方法。
可以利用事件同步线程。
2 事件编程七步曲
C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。
就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4.用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7.在适当的地方调用事件触发方法触发事件。
下面是一个简单的例子:股票价格的变化实时显示给用户

// 步骤1,定义delegate对象 public delegate void StockPriceChangedEventHanler(object sender,StockInfoEventArgs e); /// <summary> /// 事件的发布者 /// </summary> public class Publisher { // 步骤4,用event关键字定义事件对象 public event StockPriceChangedEventHanler closeEvent; // 步骤6,以调用delegate的方式写事件触发函数 protected void OnChangeFire(StockInfoEventArgs e) { if (closeEvent != null) closeEvent(this, e); } public void ShowStockClose(string code,double close) { StockInfoEventArgs e = new StockInfoEventArgs(code,close); // 步骤7,触发事件 OnChangeFire(e); } }

/// <summary> /// 事件的订阅者 /// </summary> public class Subscriber { // 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类型 public void DisplayMessage(object sender, StockInfoEventArgs e) { Console.WriteLine("StockCode:{0},Close:{1}",e.StockCode,e.Close); } }

//步骤2:定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。 public class StockInfoEventArgs : EventArgs { public string StockCode { get; set; } public double Close { get; set; } public StockInfoEventArgs(string code,double close) { this.StockCode = code; this.Close = close; } }

class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s = new Subscriber(); // 步骤5,事件的挂钩用+=操作符将事件添加到队列中 p.closeEvent += new StockPriceChangedEventHanler(s.DisplayMessage); p.ShowStockClose("000001.SZ", 10); Console.Read(); } }
3 事件的编译代码
借助Reflactor来对 event的声明语句做一探究
// 步骤4,用event关键字定义事件对象
public event StockPriceChangedEventHanler closeEvent;
我们再进一步看下CloseEvent所产生的代码:
[MethodImpl(MethodImplOptions.Synchronized)] public void add_closeEvent(StockPriceChangedEventHanler value) { this.closeEvent = (StockPriceChangedEventHanler) Delegate.Combine(this.closeEvent, value); } |
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_closeEvent(StockPriceChangedEventHanler value)
{
this.closeEvent = (StockPriceChangedEventHanler) Delegate.Remove(this.closeEvent, value);
}
private StockPriceChangedEventHanler closeEvent;
现在已经很明确了:CloseEvent事件确实是一个StockPriceChangedEventHanler类型的委托,只不过不管是不是声明为public,它总是被声明为private。另外,它还有两个方法,分别是add_closeEvent和remove_closeEvent,这两个方法分别用于注册委托类型的方法和取消注册。实际上也就是: “+= ”对应add_closeEvent,“-=”对应remove_closeEvent。而这两个方法的访问限制取决于声明事件时的访问限制符。
在add_closeEvent ()方法内部,实际上调用了System.Delegate的Combine()静态方法,这个方法用于将当前的变量添加到委托链表中。
从上可知Event它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。