C#中的event关键字提供类型安全的观察者模式实现,通过定义事件、触发事件和订阅事件实现对象间松耦合通信;使用event而非public delegate可确保封装性、防止外部触发和误操作;推荐使用EventHandler<TEventArgs>泛型委托和继承EventArgs的自定义参数类,并遵循命名规范;需注意内存泄漏、异常传播、执行顺序不确定及跨线程UI更新等潜在问题,合理取消订阅、处理异常并采用弱事件模式以提升健壮性和性能。

C#中的
event
要发布和订阅事件,你需要定义一个发布者类和一个或多个订阅者类。
发布者(Publisher)的实现:
定义一个委托(Delegate): 委托定义了事件处理方法的签名。通常,我们会使用
EventHandler
EventHandler<TEventArgs>
// 假设我们要传递一个字符串消息
public class MessageEventArgs : EventArgs
{
public string Message { get; }
public MessageEventArgs(string message)
{
Message = message;
}
}
public class Notifier
{
// 声明一个事件,类型是EventHandler<MessageEventArgs>
// 这表示事件处理方法需要接收一个object sender和MessageEventArgs e参数
public event EventHandler<MessageEventArgs>? MessageSent;
public void SendMessage(string msg)
{
Console.WriteLine($"Notifier: 准备发送消息 '{msg}'...");
// 触发事件。在C# 6.0及以后,可以使用 ?.Invoke() 安全地调用
// 传统方式是检查 MessageSent != null
MessageSent?.Invoke(this, new MessageEventArgs(msg));
Console.WriteLine("Notifier: 消息发送完成。");
}
}订阅者(Subscriber)的实现:
+=
-=
public class Listener
{
private string _name;
public Listener(string name)
{
_name = name;
}
// 事件处理方法,签名与EventHandler<MessageEventArgs>匹配
public void OnMessageReceived(object? sender, MessageEventArgs e)
{
Console.WriteLine($"{_name}: 接收到来自 {((Notifier?)sender)?.GetType().Name ?? "未知来源"} 的消息:'{e.Message}'");
}
}
// 示例用法
public class Program
{
public static void Main(string[] args)
{
Notifier notifier = new Notifier();
Listener listener1 = new Listener("监听者A");
Listener listener2 = new Listener("监听者B");
// 订阅事件
notifier.MessageSent += listener1.OnMessageReceived;
notifier.MessageSent += listener2.OnMessageReceived;
notifier.SendMessage("你好,世界!");
Console.WriteLine("
--- 监听者B取消订阅 ---
");
// 监听者B取消订阅
notifier.MessageSent -= listener2.OnMessageReceived;
notifier.SendMessage("这是一条测试消息。");
// 当程序结束或不再需要时,通常也建议取消订阅
notifier.MessageSent -= listener1.OnMessageReceived;
}
}运行上述代码,你会看到消息首先被两个监听者接收,在监听者B取消订阅后,只有监听者A会继续接收消息。
event
delegate
这是一个非常关键的问题,也是
event
public delegate MyDelegate MyEvent;
event
add
remove
封装性与安全性:
+=
-=
event
+=
-=
=
event
public delegate
public delegate MyDelegate MyEvent;
MyEvent = null;
MyEvent = SomeMethod;
MyEvent.Invoke();
线程安全(针对订阅/取消订阅):
event
add
remove
add
remove
Invoke
清晰的意图表达:
event
public delegate
所以,尽管
delegate
event
在C#中,事件处理程序和事件参数的设计遵循一套约定俗成的模式,这不仅提高了代码的可读性,也方便了框架和库的互操作性。
标准事件委托 EventHandler
EventHandler<TEventArgs>
public delegate void EventHandler(object? sender, EventArgs e);
sender
e
EventArgs.Empty
public event EventHandler? SomethingHappened;
public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e) where TEventArgs : EventArgs;
TEventArgs
System.EventArgs
public event EventHandler<DataReceivedEventArgs>? DataReceived;
自定义事件参数类(TEventArgs
当你需要向事件处理程序传递自定义数据时,必须创建一个继承自
System.EventArgs
设计原则:
get
示例:
// 假设我们有一个下载器,下载完成时需要传递文件路径和大小
public class DownloadCompletedEventArgs : EventArgs
{
public string FilePath { get; }
public long FileSize { get; }
public DateTime CompletionTime { get; }
public DownloadCompletedEventArgs(string filePath, long fileSize)
{
FilePath = filePath;
FileSize = fileSize;
CompletionTime = DateTime.Now;
}
}
public class Downloader
{
public event EventHandler<DownloadCompletedEventArgs>? DownloadCompleted;
public void StartDownload(string url)
{
Console.WriteLine($"开始下载 {url}...");
// 模拟下载过程
System.Threading.Thread.Sleep(1000);
string filePath = $"C:\Downloads\{Path.GetFileName(url)}";
long fileSize = new Random().Next(1024 * 1024, 10 * 1024 * 1024); // 1MB to 10MB
Console.WriteLine($"下载完成:{filePath}, 大小:{fileSize} 字节");
DownloadCompleted?.Invoke(this, new DownloadCompletedEventArgs(filePath, fileSize));
}
}命名约定:
Click
Loaded
DownloadCompleted
On
OnButtonClick
OnDataReceived
EventArgs
ClickEventArgs
DataReceivedEventArgs
On
protected virtual
protected virtual void OnDownloadCompleted(DownloadCompletedEventArgs e)
遵循这些模式,不仅能让你的代码更符合C#社区的习惯,也使得事件机制的使用更加清晰、一致和易于维护。
事件机制虽然强大,但在不恰当的使用下,确实可能引入一些性能问题或难以调试的逻辑错误。理解这些潜在的陷阱对于编写健壮、高效的C#代码至关重要。
内存泄漏(Memory Leaks)——最常见的陷阱:
-=
-=
Dispose
WeakEventManager
事件处理程序的执行顺序不确定性:
异常处理——一个处理程序失败可能影响所有:
try-catch
protected virtual void OnDownloadCompleted(DownloadCompletedEventArgs e)
{
// 获取委托链的副本,防止在遍历过程中被修改
EventHandler<DownloadCompletedEventArgs>? handler = DownloadCompleted;
if (handler != null)
{
// 获取所有订阅者
Delegate[] delegates = handler.GetInvocationList();
foreach (EventHandler<DownloadCompletedEventArgs> del in delegates)
{
try
{
del.Invoke(this, e);
}
catch (Exception ex)
{
// 记录异常,但允许其他处理程序继续执行
Console.Error.WriteLine($"错误:事件处理程序 {del.Method.Name} 抛出异常: {ex.Message}");
// 考虑是否需要重新抛出异常,或者根据业务逻辑决定
}
}
}
}性能开销(相对较小但存在):
Invoke
跨线程调用UI更新:
Invoke
BeginInvoke
Invoke
BeginInvoke
Dispatcher
Application.Current.Dispatcher.Invoke
BeginInvoke
async/await
理解并妥善处理这些潜在问题,能够让你更有效地利用C#的事件机制,构建出稳定、高效且易于维护的应用程序。
以上就是C#的event关键字有什么作用?如何发布和订阅事件?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号