悠悠楠杉
深入理解C事件机制:从委托到观察者模式的演化
一、什么是C#事件?
在C#中,事件(Event)本质上是一种特殊形式的委托(Delegate),它遵循"发布-订阅"模型。就像现实中的杂志订阅——出版社(事件发布者)定期发出期刊,订阅者(事件监听者)会自动收到通知,但双方无需知道彼此的具体细节。
csharp
// 典型的事件声明
public class Publisher
{
public event EventHandler<EventArgs> OnPublished; // 事件声明
}
二、事件机制的三大核心要素
1. 委托类型:事件的底层支撑
委托就像类型安全的函数指针,定义事件能接受的方法签名。.NET
内置的EventHandler<T>
是最常用的事件委托类型。
csharp
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
2. 事件声明:安全的封装器
与普通委托不同,事件通过event
关键字严格限制外部访问:
- 外部只能+=
或-=
操作符
- 仅类内部可以触发事件(invoke)
csharp
// 对比委托与事件
public Action PublicDelegate; // 完全公开
public event Action PublicEvent; // 受控访问
3. 事件参数:信息的载体
建议继承EventArgs
传递附加数据,这是.NET的标准实践:
csharp
public class FileUploadedEventArgs : EventArgs
{
public string FileName { get; set; }
public long FileSize { get; set; }
}
三、典型应用场景:观察者模式实现
事件机制是观察者模式的天然实现载体。以温度监控系统为例:
csharp
// 被观察者(温度传感器)
public class TemperatureSensor
{
public event EventHandler
private void CheckTemperature()
{
// 检测到温度变化时
TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(newTemp));
}
}
// 观察者(警报器)
public class Alarm
{
public void Subscribe(TemperatureSensor sensor)
{
sensor.TemperatureChanged += OnTemperatureChanged;
}
private void OnTemperatureChanged(object sender, TemperatureChangedEventArgs e)
{
if(e.NewTemperature > 100) TriggerAlarm();
}
}
四、事件使用的黄金法则
线程安全触发:
使用null条件运算符避免竞态条件
csharp TemperatureChanged?.Invoke(this, args);
避免内存泄漏:
及时取消订阅,特别是短生命周期对象订阅长生命周期对象时
csharp sensor.TemperatureChanged -= OnTemperatureChanged;
命名规范:
- 事件委托类型以
EventHandler
结尾 - 事件参数类以
EventArgs
结尾 - 事件名采用
On
+动词过去式(如OnClicked
)
- 事件委托类型以
五、高级应用技巧
1. 自定义事件访问器
当需要控制订阅过程时,可以像属性那样自定义add/remove:
csharp
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add { _myEvent += value; LogSubscription(); }
remove { _myEvent -= value; }
}
2. 泛型事件模式
结合泛型创建通用事件处理器:
csharp
public class DataEventArgs
{
public T Data { get; set; }
}
public event EventHandler<DataEventArgs
结语
C#事件机制完美体现了"松耦合"的设计思想。理解其背后的委托本质,掌握标准的实现模式,就能在GUI开发、异步编程等场景中游刃有余。记住:事件是"做什么"的契约,而不是"怎么做"的实现——这种抽象正是其强大之处。