事件基于委托,为委托提供了一种发布/订阅机制。在 .NET 架构内到处都能看到事件。在 Windows 应用程序中,Button 类提供了 Click 事件。这类事件就是委托。触发 Click 事件时调用的处理程序方法需要得到定义,而其参数由委托类型定义。
在下面的示例代码中,事件用于连接 CarDealer 类和 Consumer 类。CarDealer 类提供了一个新车到达时触发的事件。Consumer 类订阅该事件,以获得新车到达的通知。
事件发布程序
我们从 CarDealer 类开始介绍,它基于事件提供一个订阅。CarDealer 类用 event 关键字定义了类型为 EventHandler 的 NewCarInfo 事件。在 NewCar() 方法中,通过调用 RaiseNewCarInfo 方法触发 <CarInfoEventArgs>的 NewCarInfo 事件。在 NewCar() 方法中,通过调用 RaiseNewCarInfo 方法触发 NewCarInfo 事件。这个方法的实现确认委托是否为空,如果不为空,就引发事件:
using System;
namespace Wrox.ProCSharp.Delegates
{
public class CarInfoEventArgs:EventArgs
{
public CarInfoEventArgs(string car) => Car = car;
public string Car { get; }
}
public class CarDealer
{
public event EventHandler<CarInfoEventArgs> NewCarInfo;
public void NewCar(string car)
{
Console.WriteLine($"CarDealer, new car {car}");
NewCarInfo?.Invoke(this,new CarInfoEventArgs(car));
}
}
}
注意
前面例子中使用的空传播运算符 .? 是 C#6 新增的运算符。
“
CarDealer 类提供了 EventHandler<CarInfoEventArgs>类型的 NewCarInfo 事件。作为一个约定,事件一般使用带两个参数的方法;其中第一个参数是一个对象,包含事件的发送者,第二个参数提供了事件的相关信息。第二个参数随不同的事件类型而改变。.NET1.0 为所有不同数据类型的事件定义了几百个委托。有了泛型委托 EventHandler<T> 后,就不再需要委托了。EventHandler<TEventArgs> 定义了一个处理程序,它返回 void,接受两个参数。对于 EventHandler<TEventArgs>,第一个参数必须是 object 类型,第二个参数是 T 类型。EventHandler<TEventArgs> 还定义了一个关于 T 的约束;它必须派生自基类 EventArgs,CarInfoEventArgs 就派生自基类 EventArgs:
public event EventHandler<CarInfoEventArgs> NewCarInfo;
委托 EventHandler<TEventArgs>的定义如下:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs
”
在一行上定义事件是 C# 的简化记法。编译器会创建一个 EventHandler<CarInfoEventArgs>委托类型的变量,并添加方法,以便从委托中订阅和取消订阅。该简化记法的较长形式如下所示。这非常类似于自动属性和完整属性之间的关系。对于事件,使用 add 和 remove 关键字添加和删除委托的处理程序:
private EventHandler<CarInfoEventArgs> _newCarInfo;
public event EventHandler<CarInfoEventArgs> NewCarInfo
{
add => _newCarInfo += value;
remove => _newCarInfo -= value;
}
注意
如果不仅需要添加和删除事件处理程序,定义事件的长记法就很有用,例如,需要为多个线程访问添加同步操作。WPF 控件使用长记法给事件添加冒泡和隧道功能。
CarDealer 类通过调用委托的 Invoke 方法触发事件。可以调用给事件订阅的所有处理程序。注意,与之前的多播委托一样,方法的调用顺序无法保证。为了更多地控制处理程序的调用,可以使用 Delegate 类的 GetInvocationList()方法,访问委托列表中的每一项,并独立地调用每个方法,如上所示。
NewCarInfo?.Invoke(this,new CarInfoEventArgs(car));
“
触发事件是只包含一行代码的程序。然而,这只是 C#6 的功能。在 C#6 版本之前,触发事件会更复杂。这是 C#6 之前实现的相同功能。在触发事件之前,需要检查事件是否为空。因为在进行 null 检查和触发事件之间,可以使用另一个线程把事件设置为null,所以使用一个局部变量,如下所示:
EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo;
if (newCarInfo != null)
{
newCarInfo(this, new CarInfoEventArgs(car));
}
”
在 C#6 中,所有这一切都可以使用 null 传播运算符和一个代码行取代,如前所示。
在触发事件之前,需要检查委托 NewCarInfo 是否不为空。如果没有订阅处理程序,委托就为空:
protected virtual void RaiseNewCarInfo(string car)
{
NewCarInfo?.Invoke(this,new CarInfoEventArgs(car));
}
技术群: 需要进技术群学习交流的请添加小编微信,切记备注:加群,对以上内容有什么疑问也可以直接和小编直接沟通交流!
小编微信:mm1552923
公众号:dotNet编程大全
往期推荐
Love life,love yourself
关注小编不迷路呦~