《C# 知识回顾 - 事件入门》
在C#编程中,事件(Event)是面向对象编程中实现“发布-订阅”模式的核心机制。它允许对象之间通过消息传递进行松耦合通信,是构建响应式应用程序(如GUI界面、异步任务处理等)的基础。本文将从基础概念出发,结合代码示例,系统梳理事件的定义、声明、触发与订阅的全流程,帮助开发者快速掌握这一关键特性。
一、事件的核心概念
事件本质上是委托(Delegate)的特殊应用,用于在特定条件满足时通知订阅者。其核心包含三个角色:
- 发布者(Publisher):触发事件的类,通常包含事件声明和触发逻辑。
- 订阅者(Subscriber):响应事件的类,通过方法订阅事件。
-
事件参数(EventArgs):携带事件相关数据的类,继承自
System.EventArgs
。
与直接调用方法不同,事件允许发布者在不了解订阅者具体实现的情况下通知多个对象,实现“一对多”的通信模式。
二、声明与触发事件
1. 定义事件
事件需基于委托类型声明。首先定义委托,再声明事件:
// 1. 定义委托(可复用)
public delegate void NotifyHandler(string message);
// 2. 发布者类中声明事件
public class Publisher
{
// 声明事件(private修饰符限制外部直接触发)
public event NotifyHandler OnNotify;
// 触发事件的方法
public void DoSomething()
{
Console.WriteLine("Publisher is working...");
// 检查是否有订阅者(避免null引用异常)
OnNotify?.Invoke("Task completed!");
}
}
2. 自定义事件参数
当需要传递额外数据时,可创建自定义EventArgs
类:
// 自定义事件参数类
public class CustomEventArgs : EventArgs
{
public string Data { get; }
public int Code { get; }
public CustomEventArgs(string data, int code)
{
Data = data;
Code = code;
}
}
// 更新委托与事件
public delegate void CustomNotifyHandler(object sender, CustomEventArgs e);
public class AdvancedPublisher
{
public event CustomNotifyHandler OnCustomNotify;
public void TriggerEvent()
{
OnCustomNotify?.Invoke(this, new CustomEventArgs("Error", 500));
}
}
三、订阅与处理事件
订阅者通过“+=”操作符将方法绑定到事件,并在事件触发时执行:
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
// 订阅事件
publisher.OnNotify += HandleNotification;
}
// 事件处理方法
private void HandleNotification(string message)
{
Console.WriteLine($"Subscriber received: {message}");
}
// 取消订阅(避免内存泄漏)
public void Unsubscribe(Publisher publisher)
{
publisher.OnNotify -= HandleNotification;
}
}
完整示例:
var publisher = new Publisher();
var subscriber = new Subscriber();
// 订阅
subscriber.Subscribe(publisher);
publisher.DoSomething(); // 输出:Subscriber received: Task completed!
// 取消订阅
subscriber.Unsubscribe(publisher);
publisher.DoSomething(); // 无输出
四、事件的高级用法
1. 静态事件
静态事件属于类而非实例,适用于全局通知场景:
public static class GlobalNotifier
{
public static event Action OnGlobalEvent;
public static void RaiseGlobalEvent()
{
OnGlobalEvent?.Invoke("System alert!");
}
}
// 订阅
GlobalNotifier.OnGlobalEvent += msg => Console.WriteLine(msg);
GlobalNotifier.RaiseGlobalEvent(); // 输出:System alert!
2. 异步事件处理
使用async
方法处理耗时操作,避免阻塞事件触发:
public class AsyncPublisher
{
public event Action OnAsyncEvent;
public async Task TriggerAsync()
{
await Task.Delay(1000); // 模拟耗时操作
OnAsyncEvent?.Invoke();
}
}
public class AsyncSubscriber
{
public async Task HandleAsync()
{
await Task.Run(() =>
{
Console.WriteLine("Async processing started...");
Thread.Sleep(2000);
Console.WriteLine("Async processing completed!");
});
}
}
// 使用
var publisher = new AsyncPublisher();
var subscriber = new AsyncSubscriber();
publisher.OnAsyncEvent += () => subscriber.HandleAsync().Wait();
await publisher.TriggerAsync();
3. 弱事件模式(Weak Event)
避免订阅者对象被事件持有导致内存泄漏,可通过WeakReference
或第三方库(如Prism)实现:
// 简化版弱事件实现
public class WeakEventManager
{
private List> _handlers = new List>();
public void AddHandler(Action handler)
{
_handlers.Add(new WeakReference(handler));
}
public void Raise()
{
foreach (var weakRef in _handlers)
{
if (weakRef.TryGetTarget(out var handler))
{
handler?.Invoke();
}
}
}
}
五、事件与委托的区别
事件是委托的封装,主要区别如下:
特性 | 委托(Delegate) | 事件(Event) |
---|---|---|
访问修饰符 | 通常为public | 外部只能订阅/取消订阅,不能直接触发 |
线程安全 | 需手动处理多线程问题 | 内置线程安全检查 |
使用场景 | 回调、多播委托 | 发布-订阅模式、GUI事件 |
六、最佳实践与注意事项
-
命名规范:事件名以“On”开头(如
OnValueChanged
),处理方法以“Handler”结尾(如ValueChangedHandler
)。 -
空检查:触发事件前使用
?.Invoke
避免NullReferenceException
。 - 取消订阅:在订阅者销毁时取消订阅,防止内存泄漏。
- 避免阻塞:事件处理方法中避免长时间运行操作,考虑使用异步模式。
-
线程安全:多线程环境下使用
lock
或ConcurrentDictionary
保护事件列表。
七、实际应用场景
1. WinForms/WPF中的按钮点击事件
// WinForms示例
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += Button1_Click; // 订阅点击事件
}
private void Button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Button clicked!");
}
}
2. MVVM模式中的属性变更通知
public class ViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
3. 观察者模式实现
public interface IObserver
{
void Update(string message);
}
public class Observer : IObserver
{
public void Update(string message)
{
Console.WriteLine($"Observer received: {message}");
}
}
public class Subject
{
private List _observers = new List();
public event Action OnUpdate; // 或直接使用委托
public void Attach(IObserver observer)
{
OnUpdate += observer.Update;
// _observers.Add(observer); // 传统观察者模式实现
}
public void Notify(string message)
{
OnUpdate?.Invoke(message);
// foreach (var obs in _observers) obs.Update(message);
}
}
关键词:C#事件、委托、发布-订阅模式、EventArgs、异步事件、弱事件、MVVM、观察者模式
简介:本文系统讲解C#事件机制,从基础概念到高级用法,涵盖事件声明、触发、订阅全流程,结合代码示例说明自定义事件参数、异步处理、弱事件模式等核心技巧,并对比事件与委托的区别,提供WinForms、MVVM等实际场景的应用指南,适合C#开发者巩固事件编程能力。