编辑
2025-11-29
C#
00

目录

🔥 痛点分析:资源管理的常见陷阱
💡 解决方案:ActionDisposable模式
🛠️ 核心实现
🎯 复合资源管理器
🚀 实战应用:WPF中的最佳实践
📱 场景一:Timer管理
🎯 场景二:异步任务管理
🔗 场景三:事件订阅管理
⚡ 高级技巧:延迟释放和条件释放
🎯 完整的WPF示例
⚠️ 注意事项和最佳实践
🔍 常见陷阱
✅ 最佳实践
🎯 总结

作为一名C#开发者,你是否遇到过这样的困扰:Timer没有及时释放导致内存泄漏?异步任务无法优雅取消?事件订阅忘记解除导致对象无法回收?这些看似简单的资源管理问题,往往成为项目中的"定时炸弹"。

今天给大家分享一个强大的资源管理模式——ActionDisposable,它能够统一管理各种资源,让你的代码更加健壮,彻底告别资源管理的烦恼。这不是什么高深的理论,而是一个立即可用的实战技巧!

🔥 痛点分析:资源管理的常见陷阱

在日常开发中,我们经常需要管理各种资源:

传统做法的问题:

  • Timer对象忘记Dispose,导致后台线程持续运行
  • 事件订阅没有及时解除,形成强引用链
  • 异步任务的CancellationTokenSource管理混乱
  • 多个资源分散管理,容易遗漏
C#
// ❌ 传统的问题代码 public class BadExample : IDisposable { private Timer _timer; private CancellationTokenSource _cts; public BadExample() { _timer = new Timer(Callback, null, 1000, 1000); _cts = new CancellationTokenSource(); SomeEvent += OnSomeEvent; // 容易忘记取消订阅 } // 经常忘记实现或实现不完整 public void Dispose() { _timer?.Dispose(); // 如果忘记这行呢? _cts?.Cancel(); // 如果这里抛异常呢? // SomeEvent -= OnSomeEvent; // 经常忘记这行 } }

💡 解决方案:ActionDisposable模式

ActionDisposable模式通过委托的方式,将资源释放逻辑封装成可组合、可管理的单元。

🛠️ 核心实现

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppWpfActionDisposable { /// <summary> /// ActionDisposable - 基于委托的资源管理器 /// </summary> public sealed class ActionDisposable : IDisposable { private Action _disposeAction; private bool _disposed = false; private readonly object _lock = new object(); public ActionDisposable(Action disposeAction) { _disposeAction = disposeAction ?? throw new ArgumentNullException(nameof(disposeAction)); } public void Dispose() { if (_disposed) return; lock (_lock) { if (_disposed) return; try { _disposeAction?.Invoke(); } finally { _disposeAction = null; _disposed = true; } } } public bool IsDisposed => _disposed; /// <summary> /// 创建一个空的ActionDisposable(什么都不做) /// </summary> public static ActionDisposable Empty => new ActionDisposable(() => { }); /// <summary> /// 组合多个IDisposable对象 /// </summary> public static ActionDisposable Combine(params IDisposable[] disposables) { return new ActionDisposable(() => { foreach (var disposable in disposables) { try { disposable?.Dispose(); } catch { // 忽略单个释放失败,继续释放其他资源 } } }); } } }

🎯 复合资源管理器

C#
/// <summary> /// 复合资源管理器 - 统一管理多个资源 /// </summary> public sealed class CompositeDisposable : IDisposable { private readonly List<IDisposable> _disposables = new List<IDisposable>(); private readonly object _lock = new object(); private bool _disposed = false; public void Add(IDisposable disposable) { if (disposable == null) return; lock (_lock) { if (_disposed) { disposable.Dispose(); // 如果已释放,立即释放新添加的资源 return; } _disposables.Add(disposable); } } public void Remove(IDisposable disposable) { lock (_lock) { _disposables.Remove(disposable); } } public void Dispose() { lock (_lock) { if (_disposed) return; foreach (var disposable in _disposables) { try { disposable?.Dispose(); } catch { } } _disposables.Clear(); _disposed = true; } } }

🚀 实战应用:WPF中的最佳实践

📱 场景一:Timer管理

C#
public class TimerManager : IDisposable { private readonly CompositeDisposable _disposables = new CompositeDisposable(); public void StartHeartbeat() { var timer = new Timer(HeartbeatCallback, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); // ✅ 使用ActionDisposable管理Timer new ActionDisposable(() => { timer?.Dispose(); Console.WriteLine("心跳Timer已释放"); }).AddTo(_disposables); } public void Dispose() => _disposables.Dispose(); } // 扩展方法让代码更优雅 public static class DisposableExtensions { public static T AddTo<T>(this T disposable, CompositeDisposable composite) where T : IDisposable { composite?.Add(disposable); return disposable; } }

🎯 场景二:异步任务管理

C#
public class TaskManager : IDisposable { private readonly CompositeDisposable _disposables = new CompositeDisposable(); public async Task StartLongRunningTask() { var cts = new CancellationTokenSource(); // ✅ 统一管理CancellationTokenSource var cancellationDisposable = new ActionDisposable(() => { cts?.Cancel(); cts?.Dispose(); Console.WriteLine("任务已取消并清理"); }).AddTo(_disposables); try { await DoLongRunningWork(cts.Token); } catch (OperationCanceledException) { Console.WriteLine("任务被取消"); } finally { _disposables.Remove(cancellationDisposable); } } public void Dispose() => _disposables.Dispose(); }

🔗 场景三:事件订阅管理

C#
public class EventManager : IDisposable { private readonly CompositeDisposable _disposables = new CompositeDisposable(); public void SubscribeToEvents() { // ✅ 事件订阅的优雅管理 SomeStaticEvent += OnStaticEvent; var eventDisposable = new ActionDisposable(() => { SomeStaticEvent -= OnStaticEvent; Console.WriteLine("事件订阅已取消"); }).AddTo(_disposables); // PropertyChanged事件订阅 if (someObject is INotifyPropertyChanged notifier) { notifier.PropertyChanged += OnPropertyChanged; new ActionDisposable(() => { notifier.PropertyChanged -= OnPropertyChanged; }).AddTo(_disposables); } } public void Dispose() => _disposables.Dispose(); }

⚡ 高级技巧:延迟释放和条件释放

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; namespace AppWpfActionDisposable { /// <summary> /// ActionDisposable扩展方法 /// </summary> public static class ActionDisposableExtensions { /// <summary> /// 添加到复合资源管理器 /// </summary> public static T AddTo<T>(this T disposable, CompositeDisposable composite) where T : IDisposable { composite?.Add(disposable); return disposable; } /// <summary> /// 延迟释放 /// </summary> public static ActionDisposable DelayDispose(this IDisposable disposable, TimeSpan delay, Dispatcher dispatcher = null) { var timer = new DispatcherTimer(delay, DispatcherPriority.Background, (s, e) => { ((DispatcherTimer)s).Stop(); disposable?.Dispose(); }, dispatcher ?? Dispatcher.CurrentDispatcher); timer.Start(); return new ActionDisposable(() => { timer.Stop(); disposable?.Dispose(); }); } } /// <summary> /// 复合资源管理器 /// </summary> public sealed class CompositeDisposable : IDisposable { private readonly List<IDisposable> _disposables = new List<IDisposable>(); private readonly object _lock = new object(); private bool _disposed = false; public void Add(IDisposable disposable) { if (disposable == null) return; lock (_lock) { if (_disposed) { disposable.Dispose(); return; } _disposables.Add(disposable); } } public void Remove(IDisposable disposable) { lock (_lock) { _disposables.Remove(disposable); } } public void Clear() { lock (_lock) { foreach (var disposable in _disposables) { try { disposable?.Dispose(); } catch { } } _disposables.Clear(); } } public void Dispose() { if (_disposed) return; lock (_lock) { if (_disposed) return; Clear(); _disposed = true; } } public int Count { get { lock (_lock) { return _disposables.Count; } } } } }

🎯 完整的WPF示例

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; namespace AppWpfActionDisposable { public class MainViewModel : INotifyPropertyChanged, IDisposable { private readonly CompositeDisposable _disposables = new CompositeDisposable(); private Timer _heartbeatTimer; private int _timerCount = 0; private string _statusMessage = "准备就绪"; private bool _isLoading = false; public MainViewModel() { InitializeCommands(); InitializeTimers(); SetupEventSubscriptions(); } #region Properties public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); } public bool IsLoading { get => _isLoading; set => SetProperty(ref _isLoading, value); } public int TimerCount { get => _timerCount; set => SetProperty(ref _timerCount, value); } #endregion #region Commands public ICommand StartTaskCommand { get; private set; } public ICommand StopAllCommand { get; private set; } public ICommand AddResourceCommand { get; private set; } public ICommand ShowResourceCountCommand { get; private set; } private void InitializeCommands() { StartTaskCommand = new RelayCommand(async () => await StartLongRunningTask()); StopAllCommand = new RelayCommand(() => StopAllTasks()); AddResourceCommand = new RelayCommand(() => AddMockResource()); ShowResourceCountCommand = new RelayCommand(() => ShowResourceCount()); } #endregion #region Private Methods private void InitializeTimers() { // 心跳计时器 _heartbeatTimer = new Timer(HeartbeatCallback, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); // 使用ActionDisposable管理Timer new ActionDisposable(() => { _heartbeatTimer?.Dispose(); StatusMessage = "心跳计时器已停止"; }).AddTo(_disposables); } private void SetupEventSubscriptions() { // 模拟事件订阅的管理 var eventSubscription = new ActionDisposable(() => { // 这里可以取消事件订阅 StatusMessage = "事件订阅已清理"; }); eventSubscription.AddTo(_disposables); } private void HeartbeatCallback(object state) { Application.Current?.Dispatcher.Invoke(() => { TimerCount++; StatusMessage = $"心跳计数: {TimerCount}"; }); } private async Task StartLongRunningTask() { IsLoading = true; StatusMessage = "正在执行长时间任务..."; var cts = new CancellationTokenSource(); // 使用ActionDisposable管理CancellationTokenSource var cancellationDisposable = new ActionDisposable(() => { cts?.Cancel(); cts?.Dispose(); StatusMessage = "任务已取消"; IsLoading = false; }).AddTo(_disposables); try { // 模拟长时间运行的任务 await Task.Run(async () => { for (int i = 0; i < 10; i++) { if (cts.Token.IsCancellationRequested) break; await Task.Delay(1000, cts.Token); Application.Current?.Dispatcher.Invoke(() => { StatusMessage = $"任务进度: {(i + 1) * 10}%"; }); } }, cts.Token); StatusMessage = "任务已完成"; } catch (OperationCanceledException) { StatusMessage = "任务被用户取消"; } catch (Exception ex) { StatusMessage = $"任务执行出错: {ex.Message}"; } finally { IsLoading = false; _disposables.Remove(cancellationDisposable); } } private void StopAllTasks() { _disposables.Clear(); StatusMessage = "所有任务和资源已清理"; IsLoading = false; } private void AddMockResource() { // 模拟添加资源 var mockResource = new ActionDisposable(() => { StatusMessage = $"模拟资源已释放 - {DateTime.Now:HH:mm:ss}"; }); mockResource.AddTo(_disposables); StatusMessage = $"已添加模拟资源,当前资源数: {_disposables.Count}"; } private void ShowResourceCount() { StatusMessage = $"当前管理的资源数量: {_disposables.Count}"; } #endregion #region INotifyPropertyChanged & IDisposable public event PropertyChangedEventHandler PropertyChanged; private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (!Equals(field, value)) { field = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public void Dispose() { _disposables?.Dispose(); _heartbeatTimer?.Dispose(); } #endregion } // 简单的RelayCommand实现 public class RelayCommand : ICommand { private readonly Action _execute; private readonly Func<bool> _canExecute; public RelayCommand(Action execute, Func<bool> canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true; public void Execute(object parameter) => _execute(); } }

image.png

⚠️ 注意事项和最佳实践

🔍 常见陷阱

  1. 避免循环引用:不要在ActionDisposable的释放逻辑中引用包含它的对象
  2. 异常处理:释放逻辑中要妥善处理异常,避免影响其他资源的释放
  3. 线程安全:确保释放逻辑在多线程环境下的安全性

✅ 最佳实践

C#
// ✅ 好的做法 public class GoodExample : IDisposable { private readonly CompositeDisposable _disposables = new CompositeDisposable(); public GoodExample() { // 集中管理所有资源 SetupTimer(); SetupEventSubscriptions(); SetupAsyncOperations(); } private void SetupTimer() { var timer = new Timer(Callback, null, 1000, 1000); new ActionDisposable(() => { try { timer?.Dispose(); Console.WriteLine("Timer已安全释放"); } catch (Exception ex) { // 记录日志但不抛出异常 Logger.LogError($"Timer释放失败: {ex.Message}"); } }).AddTo(_disposables); } // 一行代码释放所有资源 public void Dispose() => _disposables?.Dispose(); }

🎯 总结

ActionDisposable模式为C#开发者提供了一种优雅的资源管理方案。通过这种模式,我们可以:

  1. 统一管理:将分散的资源释放逻辑集中管理
  2. 自动清理:对象销毁时自动释放所有托管资源
  3. 异常安全:即使部分资源释放失败,也不影响其他资源

这个模式特别适合以下场景:

  • WPF/WinForms应用的ViewModel资源管理
  • Web应用中的长连接和后台任务管理
  • 游戏开发中的资源生命周期管理

金句总结:

  • "好的资源管理不是让你记住释放什么,而是让你忘记释放这件事"
  • "ActionDisposable让资源管理从手动档升级为自动档"
  • "一行Dispose(),胜过千行try-finally"

你在项目中是如何管理资源的?遇到过哪些资源泄漏的坑?欢迎在评论区分享你的经验!

觉得这个技巧有用的话,请转发给更多的.NET同行,让我们一起写出更健壮的C#代码! 🚀

相关信息

通过网盘分享的文件:AppWpfActionDisposable.zip 链接: https://pan.baidu.com/s/1wHK4oJyjJ_MJhlsf9qOg3A?pwd=b41h 提取码: b41h --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

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