点击提交按钮、在窗体内移动鼠标、按下回车键、在I/O端口接收一个字符,这些都是事件(event),它们常常触发程序中一个或多个特殊事件处理例程(event handling routine)调用。
在.NET中事件实际上是类成员,与成员属性和方法一样。FCL中几乎每个类都有事件成员。
委托
将事件与处理方法关联起来的是委托对象(delegate object)。该对象维护着事件发生时要调用的方法列表。
声明:
public delegate void Mystring ( string msg);
声明委托时,C#编译器会创建一个sealed(密封)类,并以委托标识符(MyString)命名。该类定义了一个构造函数,它接收方法(可以是静态方法或实例方法)名作为其一个参数。该类还包含另外一些方法,以支持委托维护目标方法列表。这意味着,不同回调法。一个委托可以调用多个事件处理方法。
方法必须向委托注册,以便委托能调用这个方法。只有无返回值而且接受一个字符串参数的方法才能像这个委托注册。调用委托时,他会遍历其内部调用列表,并按照方法的注册顺序调用所有的注册方法。调用多个方法的过程称为组播(multicastring)。
组播委托
class DelegataClass
{
public delegate void MyString( string s);
public static void PrintLower( string s)
{
Console.WriteLine(s.ToLower());
}
public static void PrintUpper( string s)
{
Console.WriteLine(s.ToUpper());
}
public static void Main()
{
MyString MyDel;
// regester method to be called by delegate
MyDel = new MyString(PrintLower);
MyDel += new MyString(PrintUpper);
MyDel( " Happy birthday " );
}
}
输出:
happy birthday
HAPPY BIRTHDAY
注意,+=操作符用于向调用列表增加方法。相反地,可以使用-=来从操作符中删除方法。
前面的示例中委托同步的调用每一个方法,这说明,只要有前一个方法完成操作,才会调用后面的方法。这可能存在两个问题:某个方法可能被“挂起”,永远不返回控制,或是需要长时间来处理。它们都将阻塞整个系统的运用。为了解决这个问题.NET允许委托对方法执行异步调用。在这种情况下,被调用的方法与调用的方法将在不同的线程中运行,这样一来,调用方法能通过轮询来确定被调用方法何时结束,或者让被调用方法在完成时回调一个方法。
基于委托的事件处理
抽象的讲,.NET的事件模型基于贯彻者模式(Observer Design Pettern)。这个模式定义为“对象间存在一对多依赖关系,一个对象改变状态时,依赖此的所有对象都能得到通知并自动更新”。
-
处理内置事件
MouseEventDown
和
MouseEventHandler
都是FCL中与定义的。只需要实现事件处理程序,并向委托注册即可。
+=
操作符用于注册与事件相关的方法。
MouseEventDown += new MouseEventHandler(onMouseDown);
该语句的基本结构是:
this . event += new delegate (envet handler method);
要实现事件的处理程序,就必须提供委托定义的签名。在描述MouseEventHandler委托声明的文档中可以找到该签名。
public delegate void MouseEventHandler( object sender,MonthChangedEventArgs e)
-
使用匿名方法和委托
.NET2.0
引入了一种称为匿名方法(anmoymous method) 的语言构造,有了匿名方法,就不需要单独的事件处理方法;相反,事件处理代码封装在委托代中。
this .MouseDown += new MouseEventHandler(OnMouseDown);
替换为下面的语句,这条语句会创建一个委托,其中包含调用委托时要执行的代码。
this .MouseDown += delegate ( object sender,EventArgs e)
{
lastX = e.X;
lastY = e.Y;
}
这个取代OnMouseDown的代码块不需要方法名,故称为匿名方法。其语法如下:
delegate [(parameter - list)]{anonymose - method - block}
-
关键字delegate放在调用委托时要执行的代码前面。
-
参数列表(可选)用于像代码块传递数据。参数必须与委托声明的的参数相匹配。
-
当C#编译器遇到匿名代码块时,将创建一个新,并在其中建立一个方法包含这个代码块。调用委托时会调用此方法。
public class TestDelegeteClass
{
// delegate declaretion
public delegate void MyString ( string s);
public static void Main()
{
// register two anonymose methods with the delegate
MyString myDel;
myDel = delegate ( string s) { Console.WriteLine(s.ToLower()); };
myDel = + delegate ( string s) { Console.WriteLine(s.ToUpper()); };
// invoke delegate
myDel( " My Name is Jiero " );
}
}
调用委托时,它执行两个匿名方法中的代码,其结果就是将输入字符串分别全大写和全小写输出。
-
定义委托来处理事件
委托签名应当定义一个void返回类型,而且要由Object和EventArgs类型的参数。sender参数用来标识事件发布者,这样客户就可以使用一个方法来处理和识别源于多方的事件。
.NET
提供了EventArgs类作为保存参数列表的泛型容器。这样做有几个好处,最重要的就是它使事件处理方法与事件发布者就诶出耦合。
要创建一个EventArgs类型用作参数,需要定义一个继承自EventArgs的新类。下面的例子中包含了一个string成员属性。这个成员属性的值在触发事件之前设置,在该事件中将把这个成员属性作为一个参数。
public class IOEventArgs : EventArgs
{
private string eventMsg;
public IOEventArgs( string msg){
this .eventMsg = msg;
}
public string Msg{
get { return eventMsg; }
}
}
-
它必须继承与EventArgs类。
-
类名应当以EventArgs结尾。
-
参数要定义为只读(readonly)字段或成员属性。
-
使用构造函数来初始化值。
如果事件不生成数据,就没有必要创建一个类来作为EventArgs参数。而只需要简单的传递。EventArgs.Empty即可。
如果委托使用了EventHandler签名,可将Eventhandler用作委托,而不用创建你自己的委托。这是应为,EventHandler是.NET FCL的一部分,无需声明。
-
定义定制事件
在编写自己的类时,常常要定义定制事件,在某些状态发生时发出通知。使用event关键字来指定委托,该委托将在事件发生时调用。
class Class1
{
public delegate void UserRequest( object sender, EventArgs e); // 定义委托
public event UserRequest OnUserRequest; // 定义一个委托类型的事件
public void run()
{
while ( true )
{
if (Console.ReadLine() == " a " )
{ // 事件监听
OnUserRequest( this , new EventArgs()); // 产生事件
}
}
}
}
class Class2
{
static void Main( string [] args)
{
Class1 c1 = new Class1();
c1.OnUserRequest += new Class1.UserRequest(c1_OnUserRequest); // 委托实例化后绑定到事件
c1.run();
}
private static void c1_OnUserRequest( object sender, EventArgs e)
{ // 事件处理方法
Console.WriteLine( " 你触发了事件! " );
}
}