c#之事件
事件
前言:事件基于委托,为委托提供了一种发布/订阅的机制。在架构的内部到处都能看到事件。在Windows应用程序中,Button类提供了Click事件。这类事件就是委托。触发Click事件时调用的处理程序方法需要定义,其中参数由委托类型定义。
在本次的实例中。事件用于连接CarDealer(汽车代理商)类和Consumer(顾客类)类。CarDealer类提供了一个新车到达时发出的事件,Consumer类订阅了该事件,已获得新车到达的通知。
一:事件发布程序:
从CarDealer类开始,它基于事件提供一个订阅,CarDealer类用Event关键字定义了类型为EventHandler<CarInfoEventArgs>的NewCarInfo事件。在NewCar()方法中,通过调用RaiseNewCarInfo方法调用NewCarInfo事件。这个方法的实现检查委托是否为空。如果不为空,就引发事件。完整的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _17事件 { /// <summary> /// 新车类 /// </summary> public class CarInfoEventArgs : EventArgs { public string Car { get; private set; } /// <summary> /// 构造函数的定义 /// </summary> /// <param name="car"></param> public CarInfoEventArgs(string car) { this.Car = car; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _17事件 { /// <summary> /// 新车达到时触发的事件 /// </summary> public class CarDealer { //事件的定义 public event EventHandler<CarInfoEventArgs> NewCarInfo; /// <summary> /// 新车到达 /// </summary> /// <param name="car">新车的名字</param> public void NewCar(string car) { Console.WriteLine("CarDealer,new car{0}", car); //如果不为空就引发事件 RaiseNewCarInfo(car); } /// <summary> /// 事件的引发 /// </summary> /// <param name="car">新车</param> protected virtual void RaiseNewCarInfo(string car) { //事件的调用 NewCarInfo?.Invoke(this, new CarInfoEventArgs(car)); //EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo; //if (newCarInfo != null) //{ // newCarInfo(this, new CarInfoEventArgs(car)); //} } } }
CarDealer类提供了EventHandler<CarInfoEventArgs>的NewCarInfo事件。作为一个约定,事件一般使用带两个参数的方法,其中一个第一个参数是一个对象,包含事件的发送者,第二个参数提供了事件的相关的信息。 第二个参数岁随不同的事件类型而不同。有了泛型委托EventHandler<T>,这就不再需要委托了。EventHandler<TEventArgs>定义了一个处理程序,它返回void,接收两个参数。第一个参数的类型必须是object类型,第二个参数是T类型。EventHandler<TEventArgs>还定义了一个关于T的约束:它必须派生自基类EventArgs,CarInfoEventArgs就派生自基类EventArgs。
CarDealer类在RaiseNewCarInfo方法中触发事件。使用委托NewCarInfo和花括号可以调用给事件订阅的所有处理程序。注意和多播委托一样,方法的调用顺序无法保证。为了更多的控制处理程序的调用,可以使用Delegate类的GetInvocationList()方法,访问委托列表中的每一项,并独立的调用每个方法。
在触发事件之前,需要检查委托NewCarInfo是否为空。如果没有订阅处理程序,委托就是空,代码如下:
/// <summary> /// 事件的引发 /// </summary> /// <param name="car">新车</param> protected virtual void RaiseNewCarInfo(string car) { //事件的调用 NewCarInfo?.Invoke(this, new CarInfoEventArgs(car)); //EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo; //if (newCarInfo != null) //{ // newCarInfo(this, new CarInfoEventArgs(car)); //} }
二:事件的监听器:
Consumer类用作事件的监听器。这个类订阅了CarDealer类的事件,并定义了NewCarIsHere方法,该方法满足于EventHandler<CarInfoEventArgs>委托的要求,其参数就是object和CarInfoEventArgs类型。完整的代码实现如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _17事件 { /// <summary> /// 订阅该事件,以获得新车到达的通知 /// </summary> public class Consumer { private string _name; public string Name { get { return _name; } set { _name = value; } } /// <summary> /// 进行初始化赋值 /// </summary> /// <param name="name"></param> public Consumer(string name) { this.Name = name; } /// <summary> /// 事件的订阅 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine("{0}: car {1} is new", this.Name, e.Car); } } }
现在需要连接事件发布程序和订阅器了。为此使用CarDealer类的NewCarInfo事件,通过“+=”来创建一个订阅。消费者michael(变量)订阅了事件,接着sebastian也订阅了该事件,然后michael通过“-=”取消了订阅。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _17事件 { class Program { static void Main(string[] args) { //创建一个汽车代理商 CarDealer dealer = new CarDealer(); //顾客:迈克尔 Consumer michael = new Consumer("迈克尔"); //迈克尔订阅了事件 dealer.NewCarInfo += michael.NewCarIsHere; //顾客:塞巴斯蒂安 Consumer sebastian = new Consumer("塞巴斯蒂安"); //塞巴斯蒂安订阅了事件 dealer.NewCarInfo += sebastian.NewCarIsHere; //新车来了:法拉利 dealer.NewCar("法拉利"); //新车来了:梅塞德斯牌汽车 dealer.NewCar("梅塞德斯牌汽车"); //顾客(迈克尔)取消了事件的订阅 //dealer.NewCarInfo -= michael.NewCarIsHere; //新车来了:红牛车队 dealer.NewCar("红牛车队"); //新车来了:奥迪 //dealer.NewCar("奥迪"); Console.ReadKey(); } } }
截图如下: