编辑
2026-01-13
C#
00

目录

🔥 C#事件订阅管理器:一个类解决内存泄漏噩梦
🚨 问题分析:事件订阅的隐形杀手
常见的内存泄漏场景
传统解决方案的痛点
💡 解决方案:统一事件订阅管理器
⚠️ 常见坑点提醒
1. 泛型事件处理器的支持
2. 线程安全考虑
3. 避免重复订阅
完整模板
🎯 核心要点总结

🔥 C#事件订阅管理器:一个类解决内存泄漏噩梦

你是否遇到过这样的情况:应用程序运行一段时间后内存占用越来越高,最终导致系统卡顿甚至崩溃?如果你的项目中大量使用了事件订阅,那么很可能就是事件订阅未正确取消导致的内存泄漏问题。

今天我将分享一个实用的C#事件订阅管理器,它能够自动管理所有事件订阅的生命周期,彻底告别手动取消订阅的烦恼。这个工具类不仅代码简洁,而且在实际项目中屡试不爽,堪称防止内存泄漏的利器

🚨 问题分析:事件订阅的隐形杀手

常见的内存泄漏场景

在C#开发中,事件订阅导致的内存泄漏是最容易被忽视却又最致命的问题之一:

c#
namespace AppEventSubscriptionManager { public class Order { public int OrderId { get; } public string CustomerName { get; } public DateTime OrderDate { get; } public Order(int orderId, string customerName) { OrderId = orderId; CustomerName = customerName; OrderDate = DateTime.Now; } } public class OrderCreatedEventArgs : EventArgs { public Order Order { get; } public OrderCreatedEventArgs(Order order) { Order = order; } } public class OrderService { public event EventHandler<OrderCreatedEventArgs> OrderCreated; public void CreateOrder(Order order) { Console.WriteLine($"创建订单: {order.OrderId} for {order.CustomerName}"); OrderCreated?.Invoke(this, new OrderCreatedEventArgs(order)); } } public class NotificationService { public NotificationService(OrderService orderService) { // ⚠️ 危险:只订阅,不取消订阅 orderService.OrderCreated += OnOrderCreated; } private void OnOrderCreated(object sender, OrderCreatedEventArgs e) { Console.WriteLine($"通知: 订单 {e.Order.OrderId} 已创建,客户: {e.Order.CustomerName},日期: {e.Order.OrderDate}"); } } internal class Program { static void Main(string[] args) { // 创建订单服务和通知服务的实例 OrderService orderService = new OrderService(); NotificationService notificationService = new NotificationService(orderService); Order newOrder = new Order(1, "张三"); orderService.CreateOrder(newOrder); Order anotherOrder = new Order(2, "李四"); orderService.CreateOrder(anotherOrder); } } }

image.png

问题在哪里?NotificationService实例应该被垃圾回收时,由于OrderService仍然持有对它的事件处理器引用,导致该实例无法被正确释放,形成内存泄漏。

传统解决方案的痛点

  1. 手动管理繁琐:需要记住每个订阅都要有对应的取消订阅
  2. 容易遗漏:项目复杂时很容易忘记某个事件的取消订阅
  3. 维护成本高:代码修改时需要同时维护订阅和取消逻辑

💡 解决方案:统一事件订阅管理器

基于以上痛点,我设计了一个事件订阅管理器,它的核心思想是:

  • 统一管理:所有事件订阅都通过管理器进行
  • 自动清理:实现IDisposable接口,自动取消所有订阅
  • 简单易用:一行代码完成订阅,无需关心取消逻辑
c#
using System; using System.Collections.Generic; public class Order { public int OrderId { get; } public string CustomerName { get; } public DateTime OrderDate { get; } public Order(int orderId, string customerName) { OrderId = orderId; CustomerName = customerName; OrderDate = DateTime.Now; } } public class OrderCreatedEventArgs : EventArgs { public Order Order { get; } public OrderCreatedEventArgs(Order order) { Order = order; } } // 事件订阅管理器 - 防止内存泄漏的利器 public class EventSubscriptionManager : IDisposable { private readonly List<(object source, string eventName, Delegate handler)> _subscriptions = new List<(object, string, Delegate)>(); public void Subscribe<T>(T source, string eventName, EventHandler<OrderCreatedEventArgs> handler) where T : class { var eventInfo = typeof(T).GetEvent(eventName); eventInfo?.AddEventHandler(source, handler); _subscriptions.Add((source, eventName, handler)); } public void Dispose() { foreach (var (source, eventName, handler) in _subscriptions) { var eventInfo = source.GetType().GetEvent(eventName); eventInfo?.RemoveEventHandler(source, handler); } _subscriptions.Clear(); } } public class OrderService { public event EventHandler<OrderCreatedEventArgs> OrderCreated; public void CreateOrder(Order order) { Console.WriteLine($"创建订单: {order.OrderId} for {order.CustomerName}"); OrderCreated?.Invoke(this, new OrderCreatedEventArgs(order)); } } // 通知服务类,负责发送订单创建通知 public class NotificationService : IDisposable { private readonly EventSubscriptionManager _eventSubscriptionManager; public NotificationService(OrderService orderService, EventSubscriptionManager eventSubscriptionManager) { _eventSubscriptionManager = eventSubscriptionManager; _eventSubscriptionManager.Subscribe(orderService, nameof(orderService.OrderCreated), OnOrderCreated); } private void OnOrderCreated(object sender, OrderCreatedEventArgs e) { Console.WriteLine($"通知: 订单 {e.Order.OrderId} 已创建,客户: {e.Order.CustomerName},日期: {e.Order.OrderDate}"); } public void Dispose() { _eventSubscriptionManager.Dispose(); } } public class Program { public static void Main(string[] args) { using (var eventSubscriptionManager = new EventSubscriptionManager()) { OrderService orderService = new OrderService(); NotificationService notificationService = new NotificationService(orderService, eventSubscriptionManager); Order newOrder = new Order(1, "张三"); orderService.CreateOrder(newOrder); Order anotherOrder = new Order(2, "李四"); orderService.CreateOrder(anotherOrder); } // 这里会自动调用 Dispose,取消所有的事件订阅 } }

image.png

⚠️ 常见坑点提醒

1. 泛型事件处理器的支持

当前版本只支持EventHandler类型,如果需要支持泛型事件处理器,可以这样扩展:

c#
public void Subscribe<T, TEventArgs>(T source, string eventName, EventHandler<TEventArgs> handler) where T : class where TEventArgs : EventArgs { var eventInfo = typeof(T).GetEvent(eventName); eventInfo?.AddEventHandler(source, handler); _subscriptions.Add((source, eventName, handler)); }

2. 线程安全考虑

在多线程环境中,建议添加锁保护:

c#
private readonly object _lock = new object(); public void Subscribe<T>(T source, string eventName, EventHandler handler) where T : class { lock (_lock) { var eventInfo = typeof(T).GetEvent(eventName); eventInfo?.AddEventHandler(source, handler); _subscriptions.Add((source, eventName, handler)); } }

3. 避免重复订阅

可以添加重复检查逻辑:

c#
public void Subscribe<T>(T source, string eventName, EventHandler handler) where T : class { // 检查是否已经订阅 if (_subscriptions.Any(s => ReferenceEquals(s.source, source) && s.eventName == eventName && ReferenceEquals(s.handler, handler))) { return; // 已存在,跳过 } var eventInfo = typeof(T).GetEvent(eventName); eventInfo?.AddEventHandler(source, handler); _subscriptions.Add((source, eventName, handler)); }

完整模板

c#
public class EventSubscriptionManager : IDisposable { private readonly List<(object source, string eventName, Delegate handler)> _subscriptions = new List<(object, string, Delegate)>(); private readonly object _lock = new object(); // 泛型方法,支持泛型事件处理器 public void Subscribe<T, TEventArgs>(T source, string eventName, EventHandler<TEventArgs> handler) where T : class where TEventArgs : EventArgs { if (source == null) throw new ArgumentNullException(nameof(source)); if (handler == null) throw new ArgumentNullException(nameof(handler)); lock (_lock) { // 检查是否已经订阅 if (_subscriptions.Any(s => ReferenceEquals(s.source, source) && s.eventName == eventName && ReferenceEquals(s.handler, handler))) { return; // 已存在,跳过 } var eventInfo = typeof(T).GetEvent(eventName); eventInfo?.AddEventHandler(source, handler); _subscriptions.Add((source, eventName, handler)); } } public void Dispose() { lock (_lock) { foreach (var (source, eventName, handler) in _subscriptions) { var eventInfo = source.GetType().GetEvent(eventName); eventInfo?.RemoveEventHandler(source, handler); } _subscriptions.Clear(); } } }

🎯 核心要点总结

经过以上深入剖析,让我们总结一下这个事件订阅管理器的三个核心价值

  1. 内存安全保障:通过统一管理和自动清理,彻底解决事件订阅导致的内存泄漏问题,让你的应用程序运行更稳定、更持久。
  2. 开发效率提升:告别繁琐的手动取消订阅操作,一个using语句或Dispose调用就能搞定所有事件清理工作,让代码更简洁、维护更轻松。
  3. 项目可维护性增强:将事件订阅逻辑集中管理,降低了遗漏和出错的可能性,特别适合复杂项目中多事件订阅场景的管理。

互动时间 🤔:

  1. 你在项目中遇到过哪些棘手的内存泄漏问题?
  2. 除了事件订阅,你还知道哪些常见的内存泄漏场景?

如果这篇文章对你有帮助,请转发给更多C#开发同行,让我们一起写出更优雅、更稳定的代码!

#C#开发 #编程技巧 #内存管理 #软件工程

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!