C# 中的“事件”是当对象发生某些有趣的事情时,类向该类的客户提供通知的一种方法。事件最常见的用途是用于图形用户界面;通常,表示界面中的控件的类具有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,将通知这些事件。比如在进行网络操作时,由于网络请求很耗时,因此网络请求应该为异步,调用者并不知道请求何时完成,因此就需要使用事件来注册回调函数,当网络请求完成时触发事件,从而执行对于的回调函数获取到请求的结果。
但是事件未必只用于图形界面。事件为对象提供一种通常很有用的方法来发出信号表示状态更改,这些状态更改可能对该对象的客户很有用。事件是创建类的重要构造块,这些类可在大量的不同程序中重复使用。
使用委托来声明事件。如果您尚未学习“委托教程”,您应先学习它,然后再继续。请回忆委托对象封装一个方法,以便可以匿名调用该方法。事件是类允许客户为其提供方法(事件发生时应调用这些方法)的委托的一种方法。事件发生时,将调用其客户提供给它的委托。
一般步骤:
- 声明事件 若要在类内声明事件,首先必须声明该事件的委托类型(如果尚未声明的话,注意到事件和委托类型的名字是一样的)。
public delegate void ChangedEventHandler(object sender, EventArgs e);
委托类型定义传递给处理该事件的方法的一组参数。多个事件可共享相同的委托类型,因此仅当尚未声明任何合适的委托类型时才需要执行该步骤。
接下来,声明事件本身。
public event ChangedEventHandler Changed;
声明事件的方法与声明委托类型的字段类似,只是关键字 event 在事件声明前面,在修饰符后面。事件通常被声明为公共事件,但允许任意可访问修饰符。
- 调用事件 类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。如果没有任何客户将委托与该事件挂钩,该字段将为空;否则该字段引用应在调用该事件时调用的委托。因此,调用事件时通常先检查是否为空,然后再调用事件。
if (Changed != null)
{ Changed(this, e);
}
调用事件只能从声明该事件的类内进行。
- 与事件挂钩 从声明事件的类外看,事件像个字段,但对该字段的访问是非常受限制的。只可进行如下操作:
- 在该字段上撰写新的委托。
- 从字段(可能是复合字段)移除委托。
使用 += 和 -= 运算符完成此操作。为开始接收事件调用,客户代码先创建事件类型的委托,该委托引用应从事件调用的方法。然后它使用 += 将该委托写到事件可能连接到的其他任何委托上。
// 添加"ListChanged"事件到List对象中 List.Changed += new ChangedEventHandler(ListChanged);
当客户代码完成接收事件调用后,它将使用运算符 -= 从事件移除其委托。
// 移除该事件 List.Changed -= new ChangedEventHandler(ListChanged);
简单示例 :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace delegateEventDemo
{
class Program
{
static void Main(string[] args)
{
Request req = new Request();
}
static void print(String msg)
{
System.Console.WriteLine(msg);
}
/// <summary>
/// 调用异步任务的类
/// </summary>
class Request
{
public Request()
{
AsyncRequestDemo d = new AsyncRequestDemo();
// 注册事件触发时的回调函数来接收结果
d.addCompleted += d_addCompleted;
// 模拟异步调用
d.AsyncAdd(3, 8);
}
/// <summary>
/// 异步调用的回调函数,通过事件传递回来参数
/// </summary>
/// <param name="result"></param>
void d_addCompleted(int result)
{
print("计算结果为 : " + result);
}
}
/// <summary>
/// 被调用的类,包含事件和代理
/// </summary>
class AsyncRequestDemo
{
// 代理与事件的类型名要一样
public delegate void myAsyncAddDelegate(int result);
// 与上面的代理同名的事件类型,在事件触发时会调用在main中的回调函数d_addCompleted()
public event myAsyncAddDelegate addCompleted;
/// <summary>
/// 带参数的类型
/// </summary>
/// <param name="a">被加数a</param>
/// <param name="b">被加数b</param>
/// <returns></returns>
public void AsyncAdd(int a, int b)
{
/*
* 假设这里是异步操作,当异步完成时,调用自身的addCompleted事件,并且传递结果作为参数,
* 这样在Request类中的对应回调函数就可以接收计算结果了。
*/
print(String.Format("**** 三八节快乐 **** 异步调用 AsyncAdd {0} + {1} ", a , b) );
int result = a + b;
// 将结果通过触发事件返回
this.addCompleted(result);
}
}
}
}
结果为 :