C# 事件发布程序

6bccb01a323e09d161b15c9c25500fb8.png

事件基于委托,为委托提供了一种发布/订阅机制。在 .NET 架构内到处都能看到事件。在 Windows 应用程序中,Button 类提供了 Click 事件。这类事件就是委托。触发 Click 事件时调用的处理程序方法需要得到定义,而其参数由委托类型定义。

在下面的示例代码中,事件用于连接 CarDealer 类和 Consumer 类。CarDealer 类提供了一个新车到达时触发的事件。Consumer 类订阅该事件,以获得新车到达的通知。

事件发布程序

bb66ec93dc22e7cb86d9f9a63a36334b.png

我们从 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编程大全   

往期推荐

37c3c06ec3ef47d73c19985ee9539a4e.png

Love life,love yourself

关注小编不迷路呦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值