[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;
}
}
}
posted @   Akira300000  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示