C#事件
事件基于委托,为委托提供了一种发布/订阅机制。如Windows应用程序中,Button类提供的Click事件就是委托。
1、普通事件(强事件)
在编写事件的过程中,需要构造一个事件参数类,用以存放事件的数据。其次,需要一个事件发布者和侦听者。当事件发布者发布事件时,侦听者将接受到消息。事件参数类继承自EventArgs类。在事件发布者类中申明事件,并通过侦听者进行侦听。其中CarInfoEventArgs是事件发布类,CarDealer是事件发布者,Consumer是事件侦听者。
//所有侦听器都可以使用该参数 class CarInfoEventArgs : EventArgs { public string Car { get; private set; } public CarInfoEventArgs(string car) { Car = car; } }
// 事件发布者 class CarDealer { //事件发布者的事件参数 public event EventHandler<CarInfoEventArgs> NewCarInfo; public void NewCar(string car) { Console.WriteLine("CarDealer, new car {0}", car); RaiseNewCarInfo(car); } //事件执行方法,委托对象定义和事件发生回调调用的地方。 protected virtual void RaiseNewCarInfo(string car) { if (NewCarInfo != null) { NewCarInfo(this, new CarInfoEventArgs(car)); } } }
//顾客(侦听器)调用了发布者事件的参数,提供委托(函数指针)实例给委托(函数指针)对象 class Consumer { private string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine("{0}: Car {1} is new", name, e.Car); } }
在Main函数中调用:
class Program { static void Main(string[] args) { //事件发布者 CarDealer dealer = new CarDealer(); var michael = new Consumer("Michael"); // 将委托实例赋值给了委托对象 dealer.NewCarInfo += michael.NewCarIsHere; // 事件发生,发布者调用委托对象,发布给订阅者 dealer.NewCar("Mercedes"); var nick = new Consumer("Nick"); // 增加订阅者 dealer.NewCarInfo += nick.NewCarIsHere; // 发布者多播给多个订阅者 dealer.NewCar("Ferrari"); // 减少订阅者,如但是发布者会仍然有一个引用而无法垃圾回收订阅者资源,需要一个弱引用管理器来管理。 dealer.NewCarInfo -= michael.NewCarIsHere; // 发布者发布给订阅者 dealer.NewCar("Toyota"); } }
在该种方法中,所有的事件都是强引用,当不在使用某个订阅者时,其不会释放内存。因为在委托中引用了改对象的信息。
2、弱事件
弱事件需要实现弱事件类WeakCarInfoEventManager,它继承自WindowsBase命名空间System.Windows下的WeakEventManager。弱事件模式,发布程序和侦听程序不再强连接,当不再引用侦听器后,它就会被垃圾回收。其具体实现如下:
// 弱事件管理器 class WeakCarInfoEventManager : WeakEventManager { //需要实现为单例模式,以确保唯一管理者 public static WeakCarInfoEventManager CurrentManager { get { var manager =GetCurrentManager(typeof(WeakCarInfoEventManager)) as WeakCarInfoEventManager; if (manager == null) { manager = new WeakCarInfoEventManager(); SetCurrentManager(typeof(WeakCarInfoEventManager), manager); } return manager; } } //侦听器使用这些方法连接发布程序,此处添加侦听器 public static void AddListener(object source, IWeakEventListener listener) { CurrentManager.ProtectedAddListener(source, listener); } //侦听器使用这个方法断开与发布程序的连接,此处删除侦听器 public static void RemoveListener(object source, IWeakEventListener listener) { CurrentManager.ProtectedRemoveListener(source, listener); } // 开始侦听管理事件(订阅一个方法) protected override void StartListening(object source) { (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo; } // 停止侦听管理事件(取消订阅一个方法) protected override void StopListening(object source) { (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo; } void CarDealer_NewCarInfo(object sender, CarInfoEventArgs e) { DeliverEvent(sender, e);//在侦听器中调用IWeakEventListener接口的ReceviceWeakEnvent()方法,所以侦听器需要实现接口 } }
顾客(侦听器)需要改为实现IWeakEventListener接口的ReceiveWeakEvent方法。触发事件时,从弱事件管理器中调用该方法。
class Consumer :IWeakEventListener { private string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine("{0}: Car {1} is new", name, e.Car); } //弱事件的使用还需要让阵痛期实现接口IWakeEventListener。在Consumer类中增加一个函数…… bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { NewCarIsHere(sender, e as CarInfoEventArgs); return true; } }
在Main函数中调用为:
static void Main(string[] args) { CarDealer dealer = new CarDealer(); var michael = new Consumer("Michael"); //使用弱事件管理器 WeakCarInfoEventManager.AddListener(dealer, michael); dealer.NewCar("Ferrari"); var nick = new Consumer("Sebastian"); WeakCarInfoEventManager.AddListener(dealer, nick); dealer.NewCar("Mercedes"); WeakCarInfoEventManager.RemoveListener(dealer, michael); //此时,michael侦听器已不再引用,可垃圾回收 dealer.NewCar("Red Bull Racing"); }
3、泛型弱事件管理器
泛型弱事件管理器简化了弱事件的实现。将以上代码改为泛型弱事件,事件源时CarDealer类型,事件参数为CarInfoEventArgs类型。WeakEventManager类定义了AddHandler方法订阅事件,RemoveHandler方法取消事件。实现代码如下:
static void Main(string[] args) { //事件发布者 CarDealer dealer = new CarDealer(); var michael = new Consumer("Michael"); WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", michael.NewCarIsHere); dealer.NewCar("Mercedes"); var nick = new Consumer("Nick"); WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", nick.NewCarIsHere); dealer.NewCar("Ferrari"); WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer, "NewCarInfo", michael.NewCarIsHere); dealer.NewCar("Toyota"); }
凡所有相,皆是虚妄。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?