C#学习笔记(十一):事件基础
2006-02-05 16:34 努力学习的小熊 阅读(1435) 评论(0) 编辑 收藏 举报事件类似于异常,因为它们都由对象引发。但是它们也有几个重要的区别:其中最重要的区别就是没有处理异常中try...catch类似的结构来处理事件,而是必须订阅它们。订阅一个事件的含义是提供代码,在事件发生时执行这些代码,它们称为事件处理程序。
事件处理程序本身都是简单的函数。对事件处理函数的唯一限制是它必须匹配于事件所要求的签名(返回类型和参数)。这个签名是事件定义的一部分,由一个委托指定。
处理过程:
首先,应用程序创建一个可以引发事件的对象。例如,假定应用程序是一个即时信息传送应用程序,它创建的对象表示一个远程用户的连接。当接收到通过该连接从远程用户传送来的信息时,这个连接对象会引发一个事件。
接着,应用程序订阅事件。为此,即时消息传送应用程序将定义一个函数,该函数可以与事件指定的委托类型一起使用,把这个函数的一个引用传送给事件,而事件的处理函数可以是另一个对象的方法,假定是表示显示设备的对象,当接收到信息时,该方法将显示即时信息。
引发事件后,就通知订阅器。当接收到通过连接对象传送过来的即时消息时,就调用显示设备上的事件处理方法。因为我们使用的是一个标准方法,所以引发事件的对象可以通过参数传送任何相关的信息,这样就大大增加了事件的通用性。
看下面这个例子:
using System;
using System.Timers;
namespace Ch12Ex01
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class Class1
{
static int counter = 0;
static string displayString = "This string will appear one letter at a time.";
[STAThread]
static void
{
Timer myTimer = new Timer(100);
myTimer.Elapsed += new ElapsedEventHandler(WriteChar);
myTimer.Start();
Console.ReadLine();
}
private static void WriteChar(object sender, ElapsedEventArgs e)
{
if(counter>displayString.Length-1)
return;
Console.Write(displayString[counter++%displayString.Length]);
}
}
}
用于引发事件的对象是System.Timers.Timer类的一个实例。使用一个时间段来初始化该对象,当使用Start()方法启动Timer对象时,就引发一系列事件,根据指定的时间段来引发事件。
Timer对象有一个Elapsed事件,这个事件要求的事件处理程序签名是System.Timers. ElapsedEventHandler委托类型,该委托是在.NET Framework中定义的标准委托之一,用于与下述签名匹配的函数:
void functionName(object sender, ElapsedEventArgs e)
下一个任务是把这个处理程序与事件关联起来——即订阅它。可以使用+=运算符,给事件添加一个处理程序,其形式是用事件处理程序方法初始化的一个新委托实例:
myTimer.Elapsed += new ElapsedEventHandler(functionName)
下面是如何定义和使用自己的事件。
在定义事件前,必须先定一个委托类型,以用于该事件,这个委托类型指定了事件处理方法必须遵循的签名。
public delegate void MessageHandler(string messageText);
这个委托类型称为MessageHandler,是void函数的签名,它有一个string参数。使用这个参数可以把Connection对象接收过来的即时消息发送给Display对象。
定义了委托后(或者定位现有合适的委托),就可以把事件本身定义为Connection类的一个成员。
public class Connection
{
public event MessageHandler MessageArrived;
给事件命名(这里使用的名称为MessageArrived),用event关键字和要使用的委托类型(前面定义的MessageHandler委托类型)声明它。
以这种方式声明了事件后,就可以引发它,方法是按名称来调用它,就好像它是一个其签名是由委托指定的方法一样。例如使用下面的代码引发这个事件:
MessageArrived("This is a message.");
如果定义该委托不包含任何参数,就可以使用下面的代码:
MessageArrived();
订阅事件的类是Display,它包含一个方法DisplayMessage():
public class Display
{
public void DisplayMessage(string message)
{
Console.WriteLine("Message arrived:{0}",message);
}
}
这个方法匹配于委托类型方法的签名(返回类型和参数),所以可以使用它响应MessageArrived事件。
在进行如下定义后
Connection myConnection = new Connection();
Display myDisplay = new Display();
myConnection.MessageArrived += new MessageHandler(myDisplay.DisplayMessage);
我们就可以看出,为myConnection对象的MessageArrived事件进行初始化时,因为MessageArrived事件是用MessageHandler代理类型定义的(void返回值和string参数),而Display类下的DisplayMessage方法的定义与这个委托类型方法的签名一致,所以可以将这个事件引发后要执行的方法转到这个方法上来。
下面是整个程序用到的源代码:
Connection类
using System;
using System.Timers;
namespace Ch12Ex02
{
public delegate void MessageHandler(string messageText);
public class Connection
{
public event MessageHandler MessageArrived;
private Timer pollTimer;
public Connection()
{
pollTimer = new Timer(100);
pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);
}
public void Connect()
{
pollTimer.Start();
}
public void Disconnect()
{
pollTimer.Stop();
}
private void CheckForMessage(object source, ElapsedEventArgs e)
{
Console.WriteLine("checking for new message.");
Random random = new Random();
if((random.Next(9) == 0) && (MessageArrived != null))
{
MessageArrived("Hello Mum!");
}
}
}
}
Display类
using System;
namespace Ch12Ex02
{
/// <summary>
/// Display 的摘要说明。
/// </summary>
public class Display
{
public void DisplayMessage(string message)
{
Console.WriteLine("Message arrived:{0}",message);
}
}
}
Class1类
using System;
namespace Ch12Ex02
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class Class1
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void
{
Connection myConnection = new Connection();
Display myDisplay = new Display();
myConnection.MessageArrived += new MessageHandler(myDisplay.DisplayMessage);
myConnection.Connect();
Console.ReadLine();
}
}
}