C# 事件详解
1、事件的基本概念
事件(Event)是 C# 中用于实现发布-订阅模式的重要机制,它允许对象在状态改变时通知其他对象。事件基于委托(Delegate)实现,提供了类型安全的回调机制。
核心概念:
- 发布者(Publisher):定义并触发事件的对象
- 订阅者(Subscriber):订阅并处理事件的对象
- 委托(Delegate):事件的类型定义,规定了事件处理方法的签名
- 事件参数(EventArgs):传递事件数据的容器
事件的特点:
- 封装性好:发布者只能触发事件,不能直接调用订阅者的方法
- 类型安全:基于委托,编译时检查方法签名
- 松耦合:发布者和订阅者互不依赖
- 多播支持:一个事件可以有多个订阅者
2、事件的定义和使用
2.1、定义事件
// 定义事件参数类
public class PriceChangedEventArgs : EventArgs
{
public decimal OldPrice { get; }
public decimal NewPrice { get; }
public PriceChangedEventArgs(decimal oldPrice, decimal newPrice)
{
OldPrice = oldPrice;
NewPrice = newPrice;
}
}
// 定义委托类型
public delegate void PriceChangedEventHandler(object sender, PriceChangedEventArgs e);
// 定义发布者类
public class Product
{
// 定义事件
public event PriceChangedEventHandler PriceChanged;
private decimal price;
public decimal Price
{
get => price;
set
{
if (price != value)
{
decimal oldPrice = price;
price = value;
// 触发事件
OnPriceChanged(oldPrice, value);
}
}
}
// 触发事件的方法
protected virtual void OnPriceChanged(decimal oldPrice, decimal newPrice)
{
PriceChanged?.Invoke(this, new PriceChangedEventArgs(oldPrice, newPrice));
}
}2.2、订阅和处理事件
// 订阅者类
public class PriceMonitor
{
public void Subscribe(Product product)
{
// 订阅事件
product.PriceChanged += OnPriceChanged;
}
// 事件处理方法
private void OnPriceChanged(object sender, PriceChangedEventArgs e)
{
Console.WriteLine($"价格从 {e.OldPrice} 变为 {e.NewPrice}");
}
public void Unsubscribe(Product product)
{
// 取消订阅
product.PriceChanged -= OnPriceChanged;
}
}
// 使用示例
Product product = new Product();
PriceMonitor monitor = new PriceMonitor();
monitor.Subscribe(product);
product.Price = 100; // 触发事件
product.Price = 150; // 触发事件
monitor.Unsubscribe(product);2.3、使用内置的事件模式
// 使用 EventHandler<T> 泛型委托
public class Product
{
// 使用内置的 EventHandler<T>
public event EventHandler<PriceChangedEventArgs> PriceChanged;
private decimal price;
public decimal Price
{
get => price;
set
{
if (price != value)
{
decimal oldPrice = price;
price = value;
OnPriceChanged(oldPrice, value);
}
}
}
protected virtual void OnPriceChanged(decimal oldPrice, decimal newPrice)
{
PriceChanged?.Invoke(this, new PriceChangedEventArgs(oldPrice, newPrice));
}
}3、事件的实际应用
3.1、按钮点击事件
public class Button
{
// 定义点击事件
public event EventHandler Click;
public void SimulateClick()
{
// 触发点击事件
OnClick();
}
protected virtual void OnClick()
{
Click?.Invoke(this, EventArgs.Empty);
}
}
// 使用
Button button = new Button();
button.Click += (sender, e) =>
{
Console.WriteLine("按钮被点击了");
};
button.SimulateClick();3.2、文件监控事件
public class FileWatcher
{
public event EventHandler<FileChangedEventArgs> FileChanged;
public void WatchFile(string filePath)
{
// 模拟文件监控
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(filePath);
watcher.Filter = Path.GetFileName(filePath);
watcher.Changed += (sender, e) =>
{
FileChanged?.Invoke(this, new FileChangedEventArgs(filePath));
};
watcher.EnableRaisingEvents = true;
}
}
public class FileChangedEventArgs : EventArgs
{
public string FilePath { get; }
public FileChangedEventArgs(string filePath)
{
FilePath = filePath;
}
}3.3、最佳实践
事件命名规范:
- 事件使用动词或动词短语命名(如 Click、PriceChanged)
- 事件处理方法命名为 On + 事件名(如 OnClick)
- 事件参数类命名为事件名 + EventArgs(如 PriceChangedEventArgs)
使用建议:
- 始终检查事件是否为 null 再触发(使用 ?. 操作符)
- 使用 EventHandler<T> 泛型委托简化代码
- 事件参数继承自 EventArgs
- 触发事件的方法声明为 protected virtual,便于子类重写
- 及时取消订阅事件,避免内存泄漏
C# 事件是基于委托的发布-订阅机制,提供了类型安全、松耦合的对象间通信方式。通过合理使用事件,可以构建灵活、可扩展的应用程序架构。掌握事件的定义、订阅和触发,是 C# 开发的基础技能之一。

