c#编程第十二章 事 件

上一篇 / 下一篇  2010-02-26 16:03:17

第十二章 事 

对于“事件”这个词,C#的帮助从各个角度进行了描述,比如“事件是类在发生其关注的事情时用来提供通知的一种方式”,“事件是可以通过代码响应或“处理”的操作”,“事件是操作发生时允许执行特定于应用程序的代码的机制”,“事件是对象发送的消息,以发信号通知操作的发生”,“事件是当对象发生用户关心的情况时,类将此对象通知用户的方法”等等,概括起来就是一句话“事件是软件或硬件发生的某些事情,它要求应用程序的响应”。

======================
一 不包含附加信息的事件
======================
1 定义事件委托

要使用事件首先要定义一个与事件相关的委托,因为事件发生后一定要有某个类中的方法来处理它,而委托正是由对象引用以及该对象的方法引用组成的,所以委托能够将事件绑定到用来处理它们的方法上。

★ 事件委托准则:

.NET Framework 指南指示用于事件的委托类型应采用两个参数:“对象源”参数(用于指示事件源)和特定于事件的参数(它封装有关事件的其他任何信息)。特定于事件的参数应从 EventArgs 类派生。对于不使用任何附加信息的事件,.NET Framework 提供了 EventHandler 类。

准则告诉我们用于事件的委托一定要有两个参数,第一个参数用于指示事件源,第二个参数用于指示特定事件,所谓特定事件指的是具有附加信息的事件和没有附加信息的事件,如果使用没有附加信息的事件,委托参数就使用内置的EventHandler类的形式。

★ EventHandler

EventHandler 是命名空间System中的一个预定义的委托,是专用于表示不生成数据的事件的事件处理程序方法。

语法

public delegate void EventHandler (
 Object sender,
 EventArgs e
);

参数
sender:事件源,类型为 Object,用于引发事件的实例。
e:不包含任何事件数据的 EventArgs。

解读:

语法给出了事件委托的基本形式,其中EventHandler是一个委托的名称,可以根据习惯任意更改,第一个参数中的Object表示数据类型,sender是使用该类型创建的实例名,第二个参数EventArgs是一个内置类,e 是该类的一个实例。

通过上面的学习,我们懂得了如何为事件定义一个委托,下面就定义这样一个委托。

//定义委托
public delegate void TestEventDelegate(object sender, System.EventArgs e);

2 声明事件

事件和方法一样具有签名,签名包括名称和参数列表。事件的签名使用委托的签名来表示。

声明事件需要使用 event 关键字。

语法

修饰符 event 委托名称 事件名称

public event TestEventDelegate TestEvent;

事件要放在引发事件的类中,定义一个事件引发类。

//定义事件引发类
public class EventSource
{
   public event TestEventDelegate TestEvent;
   private void RaiseTestEvent() { }
}

3 引发事件

若要引发事件,类可以调用委托,并传递所有与事件有关的参数。

private void RaiseTestEvent()
{
   TestEventDelegate temp = TestEvent;

   if (temp != null)
   {
       temp(this, new System.EventArgs());
   }
}

4 订阅事件

要接收某个事件的类可以创建一个方法来接收该事件,然后向类事件自身添加该方法的一个委托。这个过程称为“订阅事件”。

首先,接收类必须具有与事件自身具有相同签名(如委托签名)的方法。然后,该方法(称为事件处理程序)可以采取适当的操作来响应该事件。

//定义事件接收类
public class EventReceiver
{
   //定义事件处理方法
   public void ReceiveTestEvent(object sender, System.EventArgs e)
   {
       System.Console.Write("Event received from ");
       System.Console.WriteLine(sender.ToString());
   }
}

若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。

//事件订阅方法
public void Subscribe(EventSource source)
{
   TestEventDelegate temp = ReceiveTestEvent;
   source.TestEvent += temp;
}

若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。例如:

//取消订阅方法 
public void UnSubscribe(EventSource source)
{
   TestEventDelegate temp = ReceiveTestEvent;
   source.TestEvent -= temp;
}

5 整合

前面的代码不会运行,必需在入口方法中将他们整合。

 static void Main()
   {
       EventReceiver receiver = new EventReceiver();
       EventSource source = new EventSource();
       receiver.Subscribe(source);
       source.RaiseTestEvent();
   }

完整代码

//定义委托
public delegate void TestEventDelegate(object sender, System.EventArgs e);
//事件引发类
public class EventSource
{
   //声明事件
   public event TestEventDelegate TestEvent;
   //事件引发方法
   private void RaiseTestEvent()
   {
       TestEventDelegate temp = TestEvent;

       if (temp != null)
       {
           temp(this, new System.EventArgs());
       }
   }

   static void Main()
   {
       EventReceiver receiver = new EventReceiver();
       EventSource source = new EventSource();
       receiver.Subscribe(source);
       source.RaiseTestEvent();
   }

}

//事件接收类
public class EventReceiver
{
   //事件处理方法
   public void ReceiveTestEvent(object sender, System.EventArgs e)
   {
       System.Console.WriteLine("Event received from");
       System.Console.WriteLine(sender.ToString());
   }
   //事件订阅方法
   public void Subscribe(EventSource source)
   {
       TestEventDelegate temp = ReceiveTestEvent;
       source.TestEvent += temp;
   }
}

事件使用过程小结:

1 定义委托
2 定义事件引发类
(1)定义事件
(2)定义引发方法,在这个方法中,将事件分配给一个委托实例,然后调用委托。
      
3 定义事件接收类
(1)定义事件处理方法
(2)定义事件订阅方法,在这个方法中,将事件处理方法分配给委托的一个实例,然后将委托绑定到事件。      

=======================
二 包含附加信息的事件
=======================

如果使用包含附加信息的事件,委托的第二个参数应从 EventArgs 类派生。vs2005对EventArgs 类有如下说明:

★ EventArgs

EventArgs 是包含事件数据的类的基类。此类不包含事件数据,在事件引发时不向事件处理程序传递状态信息的事件会使用此类。如果事件处理程序需要状态信息,则应用程序必须从此类派生一个类来保存数据。

下面我们以台风登陆事件为例来说明,06年第8号超强台风“桑美”,中心风力17级,以每小时20公里的速度向我国东部沿海袭来,登陆地点是福建和浙江。
 
★ 定义信息保存类

台风登陆时需要说明登陆地点和风力,因此设计一个类用来保存这两项数据。

using System;
public class Typhoonclass : EventArgs
{
   public string place;
   public int level;
   public Typhoonclass(string place, int level)
   {
       this.place = place;
       this.level = level;
   }
}

★ 事件引发类

public delegate void TyphoonDelegate
(object sender,Typhoonclass e);
public class EventSource
 
   public event TyphoonDelegate TyphoonEvent;
   public void Raise(string place, int level)
  {
     TyphoonDelegate temp = TyphoonEvent;

      if (temp != null)
    {
     temp(this, new Typhoonclass(place,level));
    }
  }
}

★ 事件接收类

public class EventReceiver
{
     public void EventHandling(object sender,Typhoonclass e)
   {
       Console.WriteLine("订阅方法被" +
              sender.ToString() + "调用。");
       if (e.level < 5)
           Console.WriteLine("登陆" + e.level +
               "的风力一般,要稍加防范");
       else if (e.level < 10)
           Console.WriteLine("登陆" + e.level +
               "的风力较大,船只回港避风");
       else
           Console.WriteLine("登陆" + e.level +
               "的风力很大,船只回港,人员疏散避风");
   }

  public void Subscribe(EventSource source)
   {
       TyphoonDelegate temp = EventHandling;
       source.TyphoonEvent += temp;
   }
}

public class Mainclass
{
   public static void Main()
   {
   EventSource source = new EventSource();
   EventReceiver receiver = new EventReceiver();
   receiver.Subscribe(source);
   source.Raise("浙江",7);

   }
}

//=============完整代码=====================
using System;
public delegate void TyphoonDelegate(object sender, Typhoonclass e);
public class Typhoonclass : EventArgs
{
   public string place;
   public int level;
   public Typhoonclass(string place, int level)
   {
       this.place = place;
       this.level = level;
   }
}

public class EventSource
 
   public event TyphoonDelegate TyphoonEvent;
   public void Raise(string place, int level)
  {
     TyphoonDelegate temp = TyphoonEvent;
      if (temp != null)
    {
     temp(this, new Typhoonclass(place,level));
    }
  }
}

public class EventReceiver
{
   public void EventHandling(object sender, Typhoonclass e)
   {
       if (e.level < 5)
           Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,要稍加防范");
       else if (e.level < 10)
           Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,船只要回港避风");
       else
           Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,船只回港人员紧急疏散");
   }

  public void Subscribe(EventSource source)
   {
       TyphoonDelegate temp = EventHandling;
       source.TyphoonEvent += temp;
   }
}

public class Mainclass
{
   public static void Main()
   {
   EventSource source = new EventSource();
   EventReceiver receiver = new EventReceiver();
   receiver.Subscribe(source);
   source.Raise("上海", 3);
   source.Raise("浙江",8);
   source.Raise("福建", 12);
   }
}

======================
三 匿名方法
======================

在下面的示例中,类 TestButton 包含事件 OnClick。派生自 TestButton 的类可以选择响应 OnClick 事件,并且定义了处理事件要调用的方法。可以以委托和匿名方法的形式指定多个处理程序,匿名方法仅适用于C#2.0语法。

//--------------匿名方法1------------------
public delegate void ButtonEventHandler();
class TestButton
{
   public event ButtonEventHandler OnClick;
   public void Click()
   {
       OnClick();
   }
}

class myclass:TestButton
{
static void Main()
   {
       TestButton mb = new TestButton();
       mb.OnClick += delegate { System.Console.WriteLine("Hello, World!"); };
       mb.Click();  
   }
}

//--------------匿名方法2------------------
public delegate void ButtonEventHandler();
class TestButton
{
   public event ButtonEventHandler OnClick;
   public void Click()
   {
       ButtonEventHandler mb = OnClick;
       mb();
   }
}

class myclass : TestButton
{
   static void Main()
   {
       TestButton sb = new TestButton();
       ButtonEventHandler mb = delegate{System.Console.WriteLine("Hello, World!");};
       sb.OnClick += mb;
       sb.Click();
   }
}

======================
四 在接口中声明事件
======================
此示例说明可以在接口中声明一个事件,然后在类中实现它。

public delegate void TestDelegate();
public interface ITestInterface
{
   event TestDelegate TestEvent;
   void FireAway();
}

public class TestClass : ITestInterface
{
   public event TestDelegate TestEvent;
   public void FireAway()
   {
       if (TestEvent != null)
       {
           TestEvent();
       }
   }
}

public class MainClass
{
   static private void F()
   {
       System.Console.WriteLine("This is called when the event fires.");
   }
   static void Main()
   {
       ITestInterface i = new TestClass();
       i.TestEvent += F;
       i.FireAway();
   }
}

======================
五 事件访问器
======================

可以使用事件访问器声明事件。事件访问器使用的语法非常类似于属性访问器,它使用 add 关键字和代码块添加事件的事件处理程序,使用 remove 关键字和代码块移除事件的事件处理程序。

public delegate void TestEventDelegate();
public class EventSource
{
   private TestEventDelegate TestEventHandlers;
   public event TestEventDelegate TestEvent
   {
       add
         
               TestEventHandlers += value; 
       }
       remove
       {
               TestEventHandlers -= value;   
       }
   }
   public void RaiseTestEvent()
   {
       TestEventDelegate temp = TestEventHandlers;
       if (temp != null)
       {
           temp();
       }

   }
}

public class testMain
{
   public void DelegateMethod()
   {
       System.Console.WriteLine("事件访问器");
   }
   static void Main()
   {
       testMain a = new testMain();
       EventSource b = new EventSource();
       b.TestEvent += a.DelegateMethod;
       b.RaiseTestEvent();
   }
}

引发事件的类必须具有存储和检索处理程序的机制,才能使用事件访问器。前面的示例使用一个私有委托字段 TestEventHandlers,以及加法和减法赋值运算符在列表中添加和移除处理程序。这与没有使用访问器声明的事件的工作方式极为类似。如果事件接收器使用加法赋值运算符 (+=) 添加事件处理程序,则调用 add 访问器,并且新的处理程序在访问器中可作为局部变量命名值使用。如果使用减法赋值运算符 (-=),则调用 remove 访问器,并且要移除的处理程序可作为局部变量命名值使用。两个访问器都返回 void,因此所有返回语句都不能返回值。无论类是否声明了事件访问器,事件的订阅和取消订阅都使用相同的语法。


TAG:

 

评分:0

我来说两句

Open Toolbar