你是否遇到过这样的情况:应用程序运行一段时间后内存占用越来越高,最终导致系统卡顿甚至崩溃?如果你的项目中大量使用了事件订阅,那么很可能就是事件订阅未正确取消导致的内存泄漏问题。
今天我将分享一个实用的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);
}
}
}

问题在哪里? 当NotificationService实例应该被垃圾回收时,由于OrderService仍然持有对它的事件处理器引用,导致该实例无法被正确释放,形成内存泄漏。
基于以上痛点,我设计了一个事件订阅管理器,它的核心思想是:
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,取消所有的事件订阅
}
}

当前版本只支持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));
}
在多线程环境中,建议添加锁保护:
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));
}
}
可以添加重复检查逻辑:
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();
}
}
}
经过以上深入剖析,让我们总结一下这个事件订阅管理器的三个核心价值:
using语句或Dispose调用就能搞定所有事件清理工作,让代码更简洁、维护更轻松。互动时间 🤔:
如果这篇文章对你有帮助,请转发给更多C#开发同行,让我们一起写出更优雅、更稳定的代码!
#C#开发 #编程技巧 #内存管理 #软件工程
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!