委托和事件

来源:互联网 发布:java游戏服务器端开发 编辑:程序博客网 时间:2024/06/11 21:56
 
点击提交按钮、在窗体内移动鼠标、按下回车键、在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)。这个模式定义为“对象间存在一对多依赖关系,一个对象改变状态时,依赖此的所有对象都能得到通知并自动更新”。
  •   处理内置事件
MouseEventDownMouseEventHandler都是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(thisnew 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(" 你触发了事件!");
    }
}