事件
大约 13 分钟约 3901 字
事件
事件和委托的区别
{
Cat cat = new Cat();
//cat.Miao();//不好的玩法--依赖太重;
//1.通过定义委托属性
//2.给委托+=方法--Miao一声之后,要触发的动作
//3.特点;把要执行的动作不放在猫的内部从外面去指定
//4.多播委托+=来完成了一系列动作的转移,把猫Miao一声后的动作转移到上端
//5.去掉了猫对于其他的依赖---提高了猫的稳定性
//6.通过委托实现了观察者模式--观察者模式---把不属于我的动作转移出去---甩锅
Console.WriteLine("************************this is delegate****************************");
{
cat.MiaoAction += new Dog().Wang; //狗叫了
cat.MiaoAction += new Mouse().Run;//老鼠跑了
cat.MiaoAction += new Mother().Wispher;
cat.MiaoAction += new Baby().Cry; // 小孩哭了
//cat.MiaoAction += new Mother().Wispher;
cat.MiaoAction += new Father().Roar;
cat.MiaoAction += new Neighbor().Awake;
cat.MiaoAction += new Stealer().Hide;
//cat.MiaoAction = null;
cat.MiaoDelegate();
cat.MiaoAction();//可以执行
}
//1.复制---粘贴---增加了一个Event关键字
//2.事件--委托和事件有什么联系和区别?
//事件是委托的实例--事件是特殊的委托;
//都可以+=/-=;
//3.既然有了多播委托,为什么又搞个事件呢?
// a.安全--事件是只能在类的内部执行;
// b.就算是你的子列也不能执行当前类中的事件,只能由自己来执行;--反射呢.---可以试试
// c.事件从外面操作,只能有一个+=/-=;
Console.WriteLine("************************this is MiaoEventHanlder****************************");
{
cat.MiaoEventHanlder += new Dog().Wang; //狗叫了
cat.MiaoEventHanlder += new Mouse().Run;//老鼠跑了
cat.MiaoEventHanlder += new Mother().Wispher;
cat.MiaoEventHanlder += new Baby().Cry; // 小孩哭了
cat.MiaoEventHanlder += new Father().Roar;
cat.MiaoEventHanlder += new Neighbor().Awake;
cat.MiaoEventHanlder += new Stealer().Hide;
//cat.MiaoEventHanlde = null;
cat.MiaoEnvent();
//cat.MiaoEventHanlder.Invoke(); //不能执行
}
//Console.Read();
//区别:上面是通过多播委托来了支持---语法层面来支持
//两者从本质上来说是没有什么太大的区别;
//区别:下面是通过面向对象的方法会来完成观察者---程序设计层面来支持
Console.WriteLine("************************this is Obeserver****************************");
{
cat.observerlist.Add(new Dog()); //狗叫了
cat.observerlist.Add(new Mouse());//老鼠跑了
cat.observerlist.Add(new Baby()); // 小孩哭了
cat.observerlist.Add(new Mother());
cat.observerlist.Add(new Father());
cat.observerlist.Add(new Neighbor());
cat.observerlist.Add(new Stealer());
cat.MiaoObsever();
}
}{
/// <summary>
///猫:
/// Miao:引发了一系列的动作;
/// 从代码层面来说:代码这样写好吗?
///
/// 不好:
/// 1.职责不单一---一个类中包含的职责太多了,除了Miao 一声,依赖于其他的类太多;
/// 2.依赖太重--依赖于Dog、Mouse、....---代码不稳定;Dog、Mouse、....任何一个类的修改,都有可能会影响到这只猫;
/// 3.这完全是面向过程式编程--完全是翻译需求;
///
///
/// 改造:
/// 1.职责单一: 猫--职责:仅仅知识Miao一下;其他的动作,不是猫的动作;
/// 2.猫Miao一声后会引发一些列的动作;应该放出去,不能属于这只猫
/// 3.猫Miao一声后,触发一些列的动作---不能把这一些列的动作放在猫的内部
/// </summary>
public class Cat
{
/// <summary>
///
/// </summary>
//public void Miao()
//{
// Console.WriteLine("{0} Miao", this.GetType().Name);
// new Dog().Wang(); //狗叫了
// new Mouse().Run();//老鼠跑了
// new Baby().Cry(); // 小孩哭了
// new Mother().Wispher();
// new Father().Roar();
// new Neighbor().Awake();
// new Stealer().Hide();
//}
public Action MiaoAction = null;
/// <summary>
/// 这个方法仅仅只是Miao一声
/// 引发的动作---可以放到多播委托中去
/// </summary>
public void MiaoDelegate()
{
Console.WriteLine("{0} MiaoDelegate", this.GetType().Name);
//if (MiaoAction != null)
//{
// MiaoAction.Invoke();
//}
MiaoAction?.Invoke();//?. 如果不为null ,就执行后面的动作
}
//static 常驻内存
public event Action MiaoEventHanlder = null;
/// <summary>
/// 这个方法仅仅只是Miao一声
/// 引发的动作---可以放到多播委托中去
/// </summary>
public void MiaoEnvent()
{
Console.WriteLine("{0} MiaoEnvent", this.GetType().Name);
MiaoEventHanlder?.Invoke();//?. 如果不为null ,就执行后面的动作
}
public List<IObject> observerlist = new List<IObject>();
public void MiaoObsever()
{
Console.WriteLine("{0} MiaoObsever", this.GetType().Name);
if (observerlist.Count>0)
{
//observerlist.ForEach(item => item.DoAction());
foreach (var item in observerlist)
{
item.DoAction();
}
}
}
}
public class ChildCat : Cat
{
public void Show()
{
base.MiaoAction(); //子类中执行父类中共的多播委托--可以执行
//base.MiaoEventHanlder.Invoke();//子类中执行父类的时间--不能执行
}
}
}事件-观察者模式(发布订阅者模式)的—的几大要素
//关于事件的在Winform中的应用
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 如果点击按钮,就会触发这个方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
}
private void button1_ClickTest(object sender, EventArgs e)
{
}
/// <summary>
/// 注册
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click_1(object sender, EventArgs e)
{
}
private void btnLogin_Click(object sender, EventArgs e)
{
}
//Winform:
//登录按钮:双击--生成了一个方法:运行起来,点击按钮---触发这个方法;
//怎么完成的?
//1.按钮其实是一个Button对象实例:继承Control---Control有一个Click事件---事件( EventHandler(object? sender, EventArgs e))
//2.Form1构造函数函数中有一个InitializeComponent方法;在InitializeComponent方法中初始化Button按钮实例,个Button的实例中的Click事件+=一个动作(button1_Click方法)
//3.点击按钮---触发事件---触发事件就是执行注册事件的方法
//其实是鼠标点击左键,就可以触发;为什么要这样设计呢,为什么一定要使用事件呢?
//1.程序运行----句柄被监听---监听鼠标的点击的动作---触发操作系统---操作系统就要去找这个句柄是在哪个应用程序中---控件---执行这个控件中的事件---触发了方法
//2.在按钮点击触发方法的设计中---有很多相同的逻辑(指向按钮,按钮会变色)+很多不同的业务逻辑;就把不变的业务逻辑封装---代码重用,可变的业务逻辑对外发布一个事件,由外部给事件注册动作;需要执行动作的时候;就把不变的,封装在内部的业务逻辑执行;还执行事件(把外部注册到事件上的动作给执行了);注册事件什么动作,就执行什么动作----可以让不同的场景共用相同的业务逻辑,分贝指定各自不同的业务逻辑;
//3.框架设计的时候,是非常需要这种设计的;----ASP.NET MVC5管道处理模型;---就是通过19大事件来完成的;
}
}标准事件的定义
{
EventStandard1.Show();
EventStandard2.Show();
//1.初始化发布者和订阅者之间的关系
EventStandard1.Init();
EventStandard1.Show();
}{
/// <summary>
///
///
///
/// </summary>
public class EventStandard1
{
///发布者的实例---公众号的实例已存在
private static PublicNumber publicNumber = new PublicNumber()
{
Id = 123,
Name = "NET社区"
};
public static void Show()
{
publicNumber.PublishShow();//再公众号发布了一片文章
}
//订阅:关联发布者和订阅者之间的关系 (初始化发布者和订阅者之间的关系)
public static void Init()
{
{
///订阅者的实例
Teacher teacher = new Teacher()
{
Id = 234,
Name = "Richard老师"
};
//订阅者的实例
NETer nETer = new NETer()
{
Id = 345,
Name = "小菜同学"
};
//建立发布者和订阅者之间的关系
publicNumber.Push += teacher.Read;
publicNumber.Push += nETer.AddTeache;
}
}
}
/// <summary>
///
/// 发布者:对完发布事件;当触发一个动作后,触发这个事件
/// </summary>
public class PublicNumber
{
public int Id { get; set; }
public string Name { get; set; }
public void PublishShow()
{
Console.WriteLine("发布了一片关于.NET5的文章。。。包含了环境安装的文档。。。");
Push.Invoke(this, new MessageInfo()
{
Id = 567,
Title = ".NET5的文章。。",
Description = "发布了一片关于.NET5的文章。。。包含了环境安装的文档。。。",
TeacherWechatNum = "MrRichard2020"
});
}
public event EventHandler Push;
}
/// <summary>
/// 订阅者:对发布者发布的事情关注
/// </summary>
public class Teacher
{
//public Teacher(PublicNumber publicNumber)
//{
// publicNumber.Push += this.Read;
//}
public int Id { get; set; }
public string Name { get; set; }
public void Read(object? sender, EventArgs e)
{
MessageInfo info = (MessageInfo)e;
Console.WriteLine("内容干货满满。。。。。");
}
}
/// <summary>
/// 订阅者:对发布者发布的事情关注
/// </summary>
public class NETer
{
public int Id { get; set; }
public string Name { get; set; }
public void AddTeache(object? sender, EventArgs e)
{
MessageInfo info = (MessageInfo)e;
Console.WriteLine("内容很多干货。。。");
Console.WriteLine($"添加了助教老师的微信{info.TeacherWechatNum}。。。去获取更多");
}
}
public class MessageInfo : EventArgs
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string TeacherWechatNum { get; set; }
}
}{
public class EventStandard2
{
public static void Show()
{
AdvancedClass frameworkClass = new AdvancedClass()
{
Id = 1,
Name = "高级班VIP课程",
Price = 4299
};
//订阅:就是个订户和事件发布者关联起来
frameworkClass.PriceIncrease += new StudentInfo().Buy;
frameworkClass.PriceIncrease += new Tencent().Extension;
frameworkClass.Price = 6299;
}
/// <summary>
/// 订户: 关注事件,事件触发后,自己做出相应的动作
/// </summary>
public class StudentInfo
{
public void Buy(object sender, EventArgs e)
{
AdvancedClass frameworkClass = (AdvancedClass)sender;
AdvancedClassEventArge eventArge = (AdvancedClassEventArge)e;
Console.WriteLine($"this is {frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
Console.WriteLine("果断买了。。。");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
}
}
/// <summary>
/// 订户,关注事件,事件触发之前,做出点动作
/// </summary>
public class Tencent
{
public void Extension(object sender, EventArgs e)
{
AdvancedClass frameworkClass = (AdvancedClass)sender;
AdvancedClassEventArge eventArge = (AdvancedClassEventArge)e;
Console.WriteLine($"this is {frameworkClass.Name}");
Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
Console.WriteLine("马上推广一波。。");
}
}
public class AdvancedClassEventArge : EventArgs
{
public int OldPrice { get; set; }
public int NewPrice { get; set; }
}
/// <summary>
/// 事件的发布者,发布是按并且在满足条件的情况下,触发事件
/// </summary>
public class AdvancedClass
{
public int Id { get; set; }
public string Name { get; set; }
private int _price = 5299;
public int Price
{
get
{
return _price;
}
set
{
if (value > _price)
{
///触发一部分动作
PriceIncrease.Invoke(this, new AdvancedClassEventArge()
{
OldPrice = _price,
NewPrice = value
});
}
_price = value;
}
}
public event EventHandler PriceIncrease;
}
}
}关键知识点
- 先明确这个主题影响的是语法层、运行时层,还是性能与可维护性层。
- 学习时要同时关注语言表面写法和编译器、JIT、GC 等底层行为。
- 真正有价值的是知道“为什么这样写”和“在什么边界下不能这样写”。
项目落地视角
- 把示例改成最小可运行样例,并观察编译输出、运行结果和异常行为。
- 如果它会进入团队代码规范,最好同步补充命名约定、禁用场景和替代方案。
- 涉及性能结论时,优先用 Benchmark 或实际热点链路验证,而不是凭感觉判断。
常见误区
- 只记语法糖,不知道底层成本。
- 把适用于小样例的写法直接搬到高并发或大对象场景里。
- 忽略框架版本、语言版本和运行时差异,导致结论失真。
进阶路线
- 继续向源码、IL、JIT 行为和 BCL 实现层深入。
- 把知识点和代码评审、性能诊断、面试复盘结合起来。
- 把同类主题做横向对比,例如值类型与引用类型、迭代器与 async 状态机、反射与 Source Generator。
适用场景
- 当你准备把《事件》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在需要理解语言特性、运行时行为或 API 边界时阅读。
- 当代码开始出现性能瓶颈、可维护性问题或语义歧义时,这类主题会直接影响实现质量。
落地建议
- 先写最小可运行样例,再把结论迁移到真实业务代码。
- 同时记录这个特性的收益、限制和替代方案,避免为了“高级”而使用。
- 涉及内存、并发或序列化时,最好配合调试器或基准测试验证。
排错清单
- 先确认问题属于编译期、运行期还是语义误用。
- 检查是否存在隐式转换、装箱拆箱、闭包捕获或上下文切换等隐藏成本。
- 查看异常栈、日志和最小复现代码,优先排除使用姿势问题。
复盘问题
- 如果把《事件》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《事件》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《事件》最大的收益和代价分别是什么?
事件的高级用法
泛型事件处理
/// <summary>
/// 泛型事件系统 — 强类型事件发布/订阅
/// </summary>
public class EventBus
{
private readonly ConcurrentDictionary<Type, List<Delegate>> _handlers = new();
// 订阅事件
public void Subscribe<TEvent>(Action<TEvent> handler)
{
var handlers = _handlers.GetOrAdd(typeof(TEvent), _ => new List<Delegate>());
lock (handlers) { handlers.Add(handler); }
}
// 取消订阅
public void Unsubscribe<TEvent>(Action<TEvent> handler)
{
if (_handlers.TryGetValue(typeof(TEvent), out var handlers))
lock (handlers) { handlers.Remove(handler); }
}
// 发布事件
public void Publish<TEvent>(TEvent @event)
{
if (!_handlers.TryGetValue(typeof(TEvent), out var handlers)) return;
List<Delegate> snapshot;
lock (handlers) { snapshot = handlers.ToList(); }
foreach (var handler in snapshot)
{
try { ((Action<TEvent>)handler)(@event); }
catch (Exception ex) { Console.WriteLine($"事件处理异常: {ex.Message}"); }
}
}
// 异步发布
public async Task PublishAsync<TEvent>(TEvent @event)
{
if (!_handlers.TryGetValue(typeof(TEvent), out var handlers)) return;
List<Delegate> snapshot;
lock (handlers) { snapshot = handlers.ToList(); }
foreach (var handler in snapshot)
{
try { await Task.Run(() => ((Action<TEvent>)handler)(@event)); }
catch (Exception ex) { Console.WriteLine($"异步事件处理异常: {ex.Message}"); }
}
}
}
// 定义事件类型
public record OrderCreatedEvent(Guid OrderId, string CustomerId, decimal TotalAmount);
public record PaymentReceivedEvent(Guid OrderId, decimal Amount, string PaymentMethod);
public record InventoryReservedEvent(Guid OrderId, List<string> ProductIds);
// 使用
var eventBus = new EventBus();
eventBus.Subscribe<OrderCreatedEvent>(e => Console.WriteLine($"订单创建: {e.OrderId}"));
eventBus.Subscribe<PaymentReceivedEvent>(e => Console.WriteLine($"支付完成: {e.OrderId}, {e.Amount}"));
eventBus.Publish(new OrderCreatedEvent(Guid.NewGuid(), "customer-1", 99.99m));弱引用事件(防止内存泄漏)
/// <summary>
/// 弱引用事件 — 避免事件订阅导致的内存泄漏
/// </summary>
public class WeakEventManager
{
private readonly List<WeakSubscription> _subscriptions = new();
public void Subscribe<TSender, TArgs>(TSender sender, string eventName, Action<TSender, TArgs> handler)
where TSender : class
where TArgs : EventArgs
{
var eventInfo = typeof(TSender).GetEvent(eventName);
if (eventInfo == null) throw new InvalidOperationException($"事件 {eventName} 不存在");
var subscription = new WeakSubscription(sender, eventInfo, handler);
_subscriptions.Add(subscription);
}
private class WeakSubscription
{
private readonly WeakReference _targetRef;
private readonly WeakReference _handlerRef;
private readonly EventInfo _eventInfo;
private readonly Delegate _eventHandler;
public WeakSubscription(object target, EventInfo eventInfo, Delegate handler)
{
_targetRef = new WeakReference(target);
_handlerRef = new WeakReference(handler.Target);
_eventInfo = eventInfo;
// 创建中继处理函数
_eventHandler = CreateEventHandler(eventInfo, handler);
eventInfo.AddEventHandler(target, _eventHandler);
}
private Delegate CreateEventHandler(EventInfo eventInfo, Delegate originalHandler)
{
var method = originalHandler.Method;
var parameters = eventInfo.EventHandlerType!.GetMethod("Invoke")!.GetParameters();
var paramTypes = parameters.Select(p => p.ParameterType).ToArray();
// 使用动态方法中继调用
return Delegate.CreateDelegate(eventInfo.EventHandlerType, null, method);
}
}
}EventHandler 与自定义 EventArgs 最佳实践
/// <summary>
/// 标准 .NET 事件模式
/// </summary>
// 1. 使用 EventHandler<T> 而非自定义委托
// 2. EventArgs 派生类用于传递事件数据
// 3. sender 始终为事件发布者
// 4. 支持取消的事件使用 CancelEventArgs
// 标准事件定义
public class OrderService
{
// 使用 EventHandler<T> 定义事件
public event EventHandler<OrderCreatedEventArgs>? OrderCreated;
public event EventHandler<OrderStatusChangedEventArgs>? OrderStatusChanged;
// 可取消的事件
public event EventHandler<OrderCancelingEventArgs>? OrderCanceling;
protected virtual void OnOrderCreated(OrderCreatedEventArgs e)
{
OrderCreated?.Invoke(this, e);
}
protected virtual void OnOrderStatusChanged(OrderStatusChangedEventArgs e)
{
OrderStatusChanged?.Invoke(this, e);
}
protected virtual bool OnOrderCanceling(OrderCancelingEventArgs e)
{
OrderCanceling?.Invoke(this, e);
return !e.Cancel;
}
public void CreateOrder(string customerId, List<OrderItem> items)
{
var orderId = Guid.NewGuid();
// 触发事件
OnOrderCreated(new OrderCreatedEventArgs(orderId, customerId, items));
}
public void CancelOrder(Guid orderId)
{
var args = new OrderCancelingEventArgs(orderId);
if (!OnOrderCanceling(args)) return; // 被取消则不执行
// 执行取消逻辑
OnOrderStatusChanged(new OrderStatusChangedEventArgs(orderId, "Cancelled"));
}
}
// 标准 EventArgs 定义
public class OrderCreatedEventArgs : EventArgs
{
public Guid OrderId { get; }
public string CustomerId { get; }
public IReadOnlyList<OrderItem> Items { get; }
public DateTime CreatedAt { get; }
public OrderCreatedEventArgs(Guid orderId, string customerId, List<OrderItem> items)
{
OrderId = orderId;
CustomerId = customerId;
Items = items.AsReadOnly();
CreatedAt = DateTime.UtcNow;
}
}
public class OrderStatusChangedEventArgs : EventArgs
{
public Guid OrderId { get; }
public string NewStatus { get; }
public DateTime ChangedAt { get; }
public OrderStatusChangedEventArgs(Guid orderId, string newStatus)
{
OrderId = orderId;
NewStatus = newStatus;
ChangedAt = DateTime.UtcNow;
}
}
// 可取消的 EventArgs
public class OrderCancelingEventArgs : EventArgs
{
public Guid OrderId { get; }
public bool Cancel { get; set; }
public string? Reason { get; set; }
public OrderCancelingEventArgs(Guid orderId)
{
OrderId = orderId;
}
}线程安全的事件触发
/// <summary>
/// 线程安全的事件触发模式
/// </summary>
public class ThreadSafeEventPublisher
{
// 方式1:使用 Volatile.Read 确保读取最新引用
private EventHandler<MyEventArgs>? _myEvent;
public event EventHandler<MyEventArgs>? MyEvent
{
add { lock (_lock) { _myEvent += value; } }
remove { lock (_lock) { _myEvent -= value; } }
}
private readonly object _lock = new();
protected virtual void OnMyEvent(MyEventArgs e)
{
// 线程安全 — 复制引用后再调用
var handler = Volatile.Read(ref _myEvent);
handler?.Invoke(this, e);
}
// 方式2:使用 Interlocked.CompareExchange
private EventHandler<MyEventArgs>? _myEvent2;
protected virtual void OnMyEvent2(MyEventArgs e)
{
var handler = _myEvent2;
handler?.Invoke(this, e);
}
}
// EventArgs 定义
public class MyEventArgs : EventArgs
{
public string Message { get; }
public MyEventArgs(string message) { Message = message; }
}