[C#] 事件和委托
事件 Event
事件模型的5个部分
事件的拥有者 (source, 对象) --> Customer
事件成员 (event, 成员) --> Order
声明
// 声明委托类型, 与类同级 // 参数1: 事件拥有者,或者说触发事件的对象,也就是顾客 // 参数2: 事件所携带的信息, 也就是点菜的内容 public delegate void OrderEventHandler(Customer c, OrderEventArgs e); // Customer类里, 声明事件 class Customer { // 声明一个委托类型的字段, 且不能被外界访问 private OrderEventHanlder orderEventHandler; public event OrderEventHandler Order { // 添加事件 add { this.orderEventHandler += value; } // 移除事件 remove { this.orderEventHandler -= value; } } // ... }
注意上面声明一个委托类型时, 第二个参数需要传入一些信息, c#有专门的类EventArgs来描述事件参数
// 事件参数, 事件所携带的信息 public class OrderEventArgs : EventArgs { // 声明属性 public string DishName { get; set; } public string Size { get; set; } }
也可以进行简略声明(字段式声明 field-like)⬇️
public delegate void OrderEventHandler(Customer c, OrderEventArgs e); // Customer类里就可以只写 class Customer { // 这时候的Order是一个委托字段 public event OrderEventHandler Order; // ... }
但是这样做是有隐患的, 因为其他的Customer类对象也可以在外部触发事件
就好比顾客1点了两盘菜, 结果结账的时候发现有别人给自己又点了两盆菜
c#为了避免这种情况, 加入了EventHandler委托(通用委托)
修改上面代码
// 首先去掉委托类型的声明 // public delegate void OrderEventHandler(Customer c, OrderEventArgs e); // Customer类里就可以只写 class Customer { // 使用通用委托类型 public event EventHandler Order; // ... }
简化声明后, 后续代码引用事件时可以直接写事件名称Order, 而不是orderEventHandler
// 检查封装的委托是否为空 if (this.Order != null)
事件的本质是委托字段的包装器(封装), 仅对外暴露添加/移除事件处理器的功能(直接使用方法名)
事件的响应者 (event subscriber, 对象) --> Waiter
事件处理器 (event handler, 成员) - 一种回调方法 --> Waiter.Action
Waiter.Action方法作为事件处理器, 会被添加到事件中去
customer.Order += waiter.Action; // 为事件添加处理器
在我们使用通用的委托类型EventHandler之前, 这个方法接受两个参数
- 参数1: 事件拥有者Customer
- 参数2: 事件的参数类型OrderEventArgs
而当我们使用EventHandler之后, 即Waiter.Action方法将作为一个通用的委托类型(大概这个意思), 这时候参数应当符合类的定义
public void Action(object sender, EventArgs e)
- 参数1: 发送/触发事件的任意对象
- 参数2: 通用的事件的参数类型EventArgs
事件订阅 - 把事件处理器与事件关联在一起
全部代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace event_demo { class Program { static void Main(string[] args) { Customer customer = new Customer(); Waiter waiter = new Waiter(); customer.Order += waiter.Action; // 为事件添加处理器 customer.Action(); // 触发事件, 同时也运行了事件处理器 customer.PayBill(); // } } // 事件类型, 事件参数 public class OrderEventArgs : EventArgs { // 声明属性 public string DishName { get; set; } public string Size { get; set; } } public class Customer { // 简化事件声明, Order就是事件的名字, 这是事件不是字段(注意), 不能用Invoke()了 public event EventHandler Order; public double Bill { get; set; } public void PayBill() { Console.WriteLine("I will pay ${0}", this.Bill); } public void WalkIn() { Console.WriteLine("Walk into the restaurant."); } public void SitDown() { Console.WriteLine("Sit down."); } public void Think() { for (int i = 0; i < 5; i++) { Console.WriteLine("I'm thinking..."); Thread.Sleep(1000); } // 触发Order事件, 传入参数 this.OnOrder("Orange Chicken", "large"); } // 事件触发器 protected void OnOrder(string dishName, string size) { // 检查封装的委托是否为空 if (this.Order != null) { OrderEventArgs e = new OrderEventArgs(); e.DishName = dishName; e.Size = size; this.Order.Invoke(this, e); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } public class Waiter { // 事件处理器, 通用版 public void Action(object sender, EventArgs e) { // 将通用版转为本项目中定义好的类对象 Customer c = sender as Customer; OrderEventArgs o = e as OrderEventArgs; Console.WriteLine("Serving the dish - {0}", o.DishName); double price = 35; switch (o.Size) { case "small": price = price * 0.5; break; case "large": price = price * 1.5; break; default: break; } c.Bill += price; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了