事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。 .NET 中的事件遵循观察者设计模式。 引发事件的类称为发布者,接收通知的类称为订阅者。 一个事件可以有多个订阅者。 通常,发布者会在发生某些操作时引发事件。 有兴趣的订阅者应该注册一个事件并处理它。
在 C# 中,事件是封装的委托。 它依赖于委托。 事件发布者和订阅者都要遵循委托的签名。 下图说明了 C# 中的事件。
一个事件可以分两步声明:
public class DemoPublisher
{
// Action是系统自带的 delegate
public event Action ProcessCompleted; // event
}
在上面的示例中,我们使用了系统的委托Action,然后使用 DemoPublisher 类中的“event”关键字声明了一个委托类型 Action 的事件 ProcessCompleted。 因此,DemoPublisher 类称为发布者。 Action 指定 ProcessCompleted 事件处理程序的签名。 它指定订阅者类中的事件处理程序方法必须具有 void 返回类型且没有参数。
现在,让我们看看如何引发 ProcessCompleted 事件。 考虑以下实现。
class Program
{
public class DemoPublisher
{
public event Action ProcessCompleted; // 事件
public void StartProcess()
{
Console.WriteLine("Process Started!");
// 其它的代码
OnProcessCompleted();
}
protected virtual void OnProcessCompleted() // 保护级别的虚方法
{
//?. 如果 ProcessCompleted不为null则调用它
ProcessCompleted?.Invoke();
}
}
}
上面的 StartProcess() 方法在最后调用了 onProcessCompleted() 方法,从而引发了一个事件。
通常,要引发事件,应使用名称 On
OnProcessCompleted() 方法使用 ProcessCompleted?.Invoke(); 调用委托。 这会调用所有注册ProcessCompleted事件的处理程序方法。
订阅者类必须注册到 ProcessCompleted 事件并使用签名与 Action 委托匹配的方法处理它,如下所示。
class Program
{
public static void Main()
{
var bl = new DemoPublisher();
bl.ProcessCompleted += bl_ProcessCompleted; // 注册事件处理方法
bl.StartProcess();
}
// event handler
public static void bl_ProcessCompleted()
{
Console.WriteLine("Process Completed!");
}
}
上面的 Program 类是 ProcessCompleted 事件的订阅者。 它使用 += 运算符向事件注册。 请记住,这与我们在多播委托的调用列表中添加方法的方式相同。 bl_ProcessCompleted() 方法处理该事件,因为它与 Action 委托的签名相匹配。
.NET 包括用于最常见事件的内置委托类型 EventHandler 和 EventHandler
上面显示的示例可以使用 EventHandler 委托来改写,如下所示。
class Program
{
public static void Main()
{
var bl = new DemoPublisher();
bl.ProcessCompleted += Bl_ProcessCompleted; ; // 注册事件处理方法
bl.StartProcess();
}
private static void Bl_ProcessCompleted(object sender, EventArgs e)
{
Console.WriteLine("Process Completed!");
}
public class DemoPublisher
{
public event EventHandler ProcessCompleted; // 事件
public void StartProcess()
{
Console.WriteLine("Process Started!");
// 其它的代码
OnProcessCompleted(EventArgs.Empty);
}
protected virtual void OnProcessCompleted(EventArgs e) // 保护级别的虚方法
{
//?. 如果 ProcessCompleted不为null则调用它
ProcessCompleted?.Invoke(this, e);
}
}
}
上面的例子我们只传递了一个空的事件参数。很多时候我们需要传递一些有用的参数给事件订阅者。 下面我就定义了一个自己的MyEventArgs。
class Program
{
public static void Main()
{
var bl = new DemoPublisher();
bl.ProcessCompleted += Bl_ProcessCompleted; ; // 注册事件处理方法
bl.StartProcess();
}
private static void Bl_ProcessCompleted(object sender, MyEventArgs e)
{
Console.WriteLine("Process " + (e.IsSuccessful ? "Completed Successfully" : "failed"));
Console.WriteLine("Completion Time: " + e.CompletionTime.ToLongDateString());
}
public class DemoPublisher
{
public event EventHandler<MyEventArgs> ProcessCompleted; // 事件
public void StartProcess()
{
Console.WriteLine("Process Started!");
// 其它的代码
var args = new MyEventArgs()
{
IsSuccessful = true,
CompletionTime = DateTime.UtcNow
};
OnProcessCompleted(args);
}
protected virtual void OnProcessCompleted(MyEventArgs e) // 保护级别的虚方法
{
//?. 如果 ProcessCompleted不为null则调用它
ProcessCompleted?.Invoke(this, e);
}
}
}
public class MyEventArgs : EventArgs
{
public bool IsSuccessful { get; set; }
public DateTime CompletionTime { get; set; }
}
使用Action来定义事件。 当一个学生注册成功的时候,我们希望给它发送一个邮件,然后还有发送优惠券。等。这些都可以放在事件里面。
class Program
{
public static void Main()
{
var service = new StudentService();
service.RegisterCompleted += SendEmail; ; // 注册事件处理方法
service.RegisterCompleted += GiveVoucher;
var s = new Student();
service.Register(s);
}
private static void GiveVoucher(Student obj)
{
Console.WriteLine("给学生赠送优惠券");
}
private static void SendEmail(Student s)
{
Console.WriteLine("发送注册成功邮件给学生");
}
public class StudentService
{
public event Action<Student> RegisterCompleted; // 事件
public void Register(Student s)
{
//把 student数据插入到数据库
OnRegisterCompleted(s);
}
protected virtual void OnRegisterCompleted(Student s) // 保护级别的虚方法
{
//?. 如果 ProcessCompleted不为null则调用它
RegisterCompleted?.Invoke(s);
}
}
}
注意这种事件的处理是同步的方式。一个事件处理方法没有完成会卡住其它的事件处理方法,和事件发布者的方法。