C# 事件机制详解与应用
事件是类或对象用来通知其他类或对象发生了某些事情的一种机制,基于委托。
事件本质上是委托的一种封装,提供更安全的订阅和取消订阅机制。
事件的使用步骤分为:声明委托、定义事件、触发事件和订阅事件。
要注意事件声明的标准模式,比如使用EventHandler委托或者自定义的委托类型,以及事件参数应该继承EventArgs。
高级部分包括事件访问器、弱事件模式、异步事件处理等。
弱事件模式是为了防止内存泄漏,特别是在长时间运行的应用中,比如WPF中的情况。
异步事件处理涉及到async和await,需要注意线程安全,比如在UI线程中更新界面需要使用Invoke。
了解常见问题,比如事件与委托的区别,如何安全触发事件,以及事件命名规范。
事件是什么
在 C# 中,事件(Event)是基于委托(Delegate)的机制,用于实现发布-订阅模式。
事件核心概念
定义:事件是类或对象用于通知其他类或对象发生了特定行为的机制
作用:解耦事件发布者(Publisher)和订阅者(Subscriber)
核心组成:
委托类型(定义事件签名)
event 关键字
事件触发方法
事件订阅者(+=)和取消订阅(-=)
事件使用步骤
步骤1. 声明委托(定义事件签名)
public delegate void MyEventHandler(object sender, EventArgs e);步骤2. 定义事件
public class Publisher
{
// 使用 event 关键字声明事件
public event MyEventHandler MyEvent;
}步骤3. 触发事件
protected virtual void OnMyEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty); // 安全触发
}步骤4. 订阅事件
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.MyEvent += HandleEvent;
}
private void HandleEvent(object sender, EventArgs e)
{
Console.WriteLine("事件被触发!");
}
}两种事件声明方式
方式1:基于自定义委托
public class Button
{
public delegate void ClickHandler(object sender, EventArgs e);
public event ClickHandler Clicked;
}方式2:基于泛型 EventHandler<T>
(推荐 .NET 2.0+ 使用)
public class Button
{
public event EventHandler<ClickEventArgs> Clicked;
}
// 自定义事件参数
public class ClickEventArgs : EventArgs
{
public int ClickCount { get; set; }
}最佳实践:事件参数类应继承自 EventArgs
实际应用场景示例
场景:温度监控报警
public class TemperatureMonitor
{
public event EventHandler<TemperatureEventArgs> Overheated;
public void CheckTemperature(float temperature)
{
if (temperature > 100)
{
OnOverheated(new TemperatureEventArgs(temperature));
}
}
protected virtual void OnOverheated(TemperatureEventArgs e)
{
Overheated?.Invoke(this, e);
}
}
public class TemperatureEventArgs : EventArgs
{
public float Temperature { get; }
public TemperatureEventArgs(float temperature)
{
Temperature = temperature;
}
}
// 订阅方
public class Alarm
{
public Alarm(TemperatureMonitor monitor)
{
monitor.Overheated += OnOverheated;
}
private void OnOverheated(object sender, TemperatureEventArgs e)
{
Console.WriteLine($"警报!当前温度:{e.Temperature}℃");
}
}高级用法
事件访问器(自定义 add/remove)
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add
{
Console.WriteLine("添加订阅");
_myEvent += value;
}
remove
{
Console.WriteLine("移除订阅");
_myEvent -= value;
}
}2. 弱事件模式(避免内存泄漏)
使用 WeakEventManager(WPF 等 UI 框架常用)
WeakEventManager<Publisher, EventArgs>
.AddHandler(publisher, nameof(Publisher.MyEvent), HandleEvent);3.异步事件处理
public event EventHandler<MyEventArgs> MyEventAsync;
private async void OnMyEventAsync()
{
var handlers = MyEventAsync?.GetInvocationList();
if (handlers != null)
{
foreach (var handler in handlers.Cast<EventHandler<MyEventArgs>>())
{
await Task.Run(() => handler(this, new MyEventArgs()));
}
}
}常见问题解答
事件与委托的区别?
事件是委托的封装,提供更安全的订阅机制
外部只能 +=/-=,不能直接 = 或 Invoke
如何安全触发事件?
MyEvent?.Invoke(this, EventArgs.Empty);
//使用空值检查避免 NullReferenceException总结
通过事件机制可以实现:
松耦合的系统架构
可扩展的通知系统
标准化的回调处理
关键点:
始终使用 EventHandler<T> 模式
事件参数继承 EventArgs
使用安全触发方式(?.Invoke)
注意事件订阅导致的内存泄漏
完整的事件生命周期:
声明 → 触发 → 订阅 → 处理 → 取消订阅