C# 事件(Event) - CSHARP教程

C# 事件(Event)

C# 事件(Event)是一种成员,用于将特定的事件通知发送给订阅者。事件通常用于实现观察者模式,它允许一个对象将状态的变化通知其他对象,而不需要知道这些对象的细节。

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。

C# 中使用事件机制实现线程间的通信。

关键点:

  • 声明委托 :定义事件将使用的委托类型。委托是一个函数签名。
  • 声明事件 :使用 event 关键字声明一个事件。
  • 触发事件 :在适当的时候调用事件,通知所有订阅者。
  • 订阅和取消订阅事件 :其他类可以通过 += -= 运算符订阅和取消订阅事件。

通过事件使用委托

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

声明事件(Event)

在类的内部声明事件,首先必须声明该事件的委托类型。例如:


public delegate void BoilerLogHandler(string status);

然后,声明事件本身,使用 event 关键字:


// 基于上面的委托定义事件

public event BoilerLogHandler BoilerEventLog;

上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件,该事件在生成的时候会调用委托。

以下示例展示了如何在 C# 中使用事件:

示例代码
usingSystem;namespaceEventDemo{// 定义一个委托类型,用于事件处理程序publicdelegatevoidNotifyEventHandler(objectsender, EventArgs e);// 发布者类publicclassProcessBusinessLogic{// 声明事件publiceventNotifyEventHandler ProcessCompleted;// 触发事件的方法protectedvirtualvoidOnProcessCompleted(EventArgs e){ProcessCompleted?.Invoke(this, e);}// 模拟业务逻辑过程并触发事件publicvoidStartProcess(){Console.WriteLine("Process Started!");// 这里可以加入实际的业务逻辑// 业务逻辑完成,触发事件OnProcessCompleted(EventArgs.Empty);}}// 订阅者类publicclassEventSubscriber{publicvoidSubscribe(ProcessBusinessLogic process){process.ProcessCompleted+=Process_ProcessCompleted;}privatevoidProcess_ProcessCompleted(objectsender, EventArgs e){Console.WriteLine("Process Completed!");}}classProgram{staticvoidMain(string[]args){ProcessBusinessLogic process=newProcessBusinessLogic();EventSubscriber subscriber=newEventSubscriber();// 订阅事件subscriber.Subscribe(process);// 启动过程process.StartProcess();Console.ReadLine();}}}

说明

1、定义委托类型:

public delegate void NotifyEventHandler(object sender, EventArgs e);

这是一个委托类型,它定义了事件处理程序的签名。通常使用 EventHandler EventHandler<TEventArgs> 来替代自定义的委托。

2、声明事件:

public event NotifyEventHandler ProcessCompleted;

这是一个使用 NotifyEventHandler 委托类型的事件。

3、触发事件:

protected virtual void OnProcessCompleted(EventArgs e)

{

    ProcessCompleted?.Invoke(this, e);

}

这是一个受保护的方法,用于触发事件。使用 ?.Invoke 语法来确保只有在有订阅者时才调用事件。

4、订阅和取消订阅事件:

process.ProcessCompleted += Process_ProcessCompleted;

订阅者使用 += 运算符订阅事件,并定义事件处理程序 Process_ProcessCompleted

实例

示例代码
usingSystem;namespaceSimpleEvent{usingSystem;/***********发布器类***********/publicclassEventTest{privateintvalue;publicdelegatevoidNumManipulationHandler();publiceventNumManipulationHandler ChangeNum;protectedvirtualvoidOnNumChanged(){if(ChangeNum!=null){ChangeNum();/* 事件被触发 */}else{Console.WriteLine("event not fire");Console.ReadKey();/* 回车继续 */}}publicEventTest(){intn=5;SetValue(n);}publicvoidSetValue(intn){if(value!=n){value=n;OnNumChanged();}}}/***********订阅器类***********/publicclasssubscribEvent{publicvoidprintf(){Console.WriteLine("event fire");Console.ReadKey();/* 回车继续 */}}/***********触发***********/publicclassMainClass{publicstaticvoidMain(){EventTest e=newEventTest();/* 实例化对象,第一次没有触发事件 */subscribEvent v=newsubscribEvent();/* 实例化对象 */e.ChangeNum+=newEventTest.NumManipulationHandler(v.printf);/* 注册 */e.SetValue(7);e.SetValue(11);}}}

当上面的代码被编译和执行时,它会产生下列结果:


event not fire

event fire

event fire



本实例提供一个简单的用于热水锅炉系统故障排除的应用程序。当维修工程师检查锅炉时,锅炉的温度和压力会随着维修工程师的备注自动记录到日志文件中。

示例代码
usingSystem;usingSystem.IO;namespaceBoilerEventAppl{// Boiler 类classBoiler{publicintTemp{get;privateset;}publicintPressure{get;privateset;}publicBoiler(inttemp,intpressure){Temp=temp;Pressure=pressure;}}// 事件发布器classDelegateBoilerEvent{publicdelegatevoidBoilerLogHandler(stringstatus);// 基于上面的委托定义事件publiceventBoilerLogHandler BoilerEventLog;publicvoidLogProcess(){stringremarks="O.K.";Boiler boiler=newBoiler(100,12);inttemp=boiler.Temp;intpressure=boiler.Pressure;if(temp>150||temp<80||pressure<12||pressure>15){remarks="Need Maintenance";}OnBoilerEventLog($"Logging Info:\nTemperature: {temp}\nPressure: {pressure}\nMessage: {remarks}");}protectedvoidOnBoilerEventLog(stringmessage){BoilerEventLog?.Invoke(message);}}// 该类保留写入日志文件的条款classBoilerInfoLogger:IDisposable{privatereadonlyStreamWriter _streamWriter;publicBoilerInfoLogger(stringfilename){_streamWriter=newStreamWriter(newFileStream(filename, FileMode.Append, FileAccess.Write));}publicvoidLogger(stringinfo){_streamWriter.WriteLine(info);}publicvoidDispose(){_streamWriter?.Close();}}// 事件订阅器publicclassRecordBoilerInfo{staticvoidLogger(stringinfo){Console.WriteLine(info);}staticvoidMain(string[]args){using(BoilerInfoLogger fileLogger=newBoilerInfoLogger("e:\\boiler.txt")){DelegateBoilerEvent boilerEvent=newDelegateBoilerEvent();boilerEvent.BoilerEventLog+=Logger;boilerEvent.BoilerEventLog+=fileLogger.Logger;boilerEvent.LogProcess();}Console.ReadLine();}}}

当上面的代码被编译和执行时,它会产生下列结果:


Logging info:



Temperature 100

Pressure 12



Message: O. K