事件
事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。
引发事件的对象称为事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。
在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。
.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。
Microsoft的产品文档定义的事件:事件是一种使对象或类能够提供通知的成员。
public class ConsoleEventArgs : EventArgs { // 控制台输出的消息 private string message; public string Message { get { return message; } } public ConsoleEventArgs() : base() { this.message = string.Empty; } public ConsoleEventArgs(string message) : base() { this.message = message; } } /// <summary> /// 管理控制台,在输出前发送输出事件 /// </summary> public class ConsoleManager { // 定义控制台事件成员对象 public event EventHandler<ConsoleEventArgs> ConsoleEvent; /// <summary> /// 控制台输出 /// </summary> public void ConsoleOutput(string message) { // 发送事件 ConsoleEventArgs args = new ConsoleEventArgs(message); SendConsoleEvent(args); // 输出消息 Console.WriteLine(message); } /// <summary> /// 负责发送事件 /// </summary> /// <param name="args">事件的参数</param> protected virtual void SendConsoleEvent(ConsoleEventArgs args) { // 定义一个临时的引用变量,确保多线程访问时不会发生问题 EventHandler<ConsoleEventArgs> temp = ConsoleEvent; if (temp != null) { temp(this, args); } } } /// <summary> /// 日志类型,负责订阅控制台输出事件 /// </summary> public class Log { // 日志文件 private const string logFile = @"C:\TestLog.txt"; public Log(ConsoleManager cm) { // 订阅控制台输出事件 cm.ConsoleEvent += this.WriteLog; } /// <summary> /// 事件处理方法,注意参数固定模式 /// </summary> /// <param name="sender">事件的发送者</param> /// <param name="args">事件的参数</param> private void WriteLog(object sender, EventArgs args) { // 文件不存在的话则创建新文件 if (!File.Exists(logFile)) { using (FileStream fs = File.Create(logFile)) { } } FileInfo fi = new FileInfo(logFile); using (StreamWriter sw = fi.AppendText()) { ConsoleEventArgs cea = args as ConsoleEventArgs; sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "|" + sender.ToString() + "|" + cea.Message); } } } class Program { static void Main(string[] args) { // 控制台事件管理者 ConsoleManager cm = new ConsoleManager(); // 控制台事件订阅者 Log log = new Log(cm); cm.ConsoleOutput("测试控制台输出事件"); cm.ConsoleOutput("测试控制台输出事件"); cm.ConsoleOutput("测试控制台输出事件"); Console.ReadKey(); } }
事件和委托有神马联系?
经常听人说,委托本质是一个类型,而事件本质是一个特殊的委托类型的实例。最好的办法莫过于通过查看原代码和编译后的IL代码进行分析。
① 回顾刚刚的代码,在ConsoleManager类中定义了一个事件成员
public event EventHandler<ConsoleEventArgs> ConsoleEvent;
EventHandler是.NET框架中提供的一种标准的事件模式.
② 下面通过Reflector来查看一下事件ConsoleEvent的IL代码(中间代码),可以更方便地看到这一点:
首先,查看EventHandler的IL代码,可以看到在C#编译器编译delegate代码时,编译后是成为了一个class。
其次当C#编译器编译event代码时首先为类型添加一个EventHandler<T>的委托实例对象,然后为其增加一对add/remove方法用来实现从委托链中添加和移除方法的功能。
通过查看add_ConsoleEvent的IL代码,可以清楚地看到订阅事件的本质是调用Delegate的Combine方法将事件处理方法绑定到委托链中。
时间: 2024-10-11 16:58:10