工厂方法中部分创建的IDisposable对象的资源清理方案及多依赖场景实现
工厂方法中部分创建的IDisposable对象的资源清理方案及多依赖场景实现
你的Stack<IDisposable>思路非常合理,这是处理这类分步创建、需回滚清理场景的经典方案之一,尤其适合依赖数量较多或不确定的情况,能有效避免嵌套try/catch带来的“金字塔代码”问题。下面结合你的代码场景,给出具体实现方案,包括基础手动处理、通用栈式方案,以及优化建议。
一、3个依赖的基础手动实现(理解核心逻辑)
如果只有3个依赖,手动嵌套try/catch也能实现,但代码冗余度高,依赖越多维护成本越高:
public static MyClass Create(int _value) { A? a = null; B? b = null; C? c = null; try { a = new A(_value); b = new B(_value); c = new C(_value); return new MyClass(a, b, c); } catch { // 逆序清理已创建的对象(避免后续对象依赖前序对象的情况) c?.Dispose(); b?.Dispose(); a?.Dispose(); // 重新抛出原异常,不丢失错误上下文 throw; } }
这种方式虽然直观,但当依赖增加到5个、10个时,需要手动添加大量?.Dispose()代码,极易遗漏,维护性很差。
二、推荐:基于Stack<IDisposable>的通用实现
你的栈式思路完美解决了多依赖的问题,核心逻辑是:每成功创建一个可释放对象就压入栈;中途创建失败时,遍历栈弹出所有对象并释放;全部创建成功后清空栈(对象生命周期转由MyClass托管)。
修正后的完整代码
首先修正MyClass的标准Dispose模式(原代码的Dispose实现不符合.NET规范),再实现栈式工厂方法:
using System.Collections.Generic; class A : IDisposable { private readonly int p_value; public A(int value) { ArgumentOutOfRangeException.ThrowIfNegative(value); p_value = value; } public void Print() => Console.WriteLine($"Value is: {p_value}"); public void Dispose() { Console.WriteLine("A disposed"); GC.SuppressFinalize(this); } } class B : IDisposable { private readonly int p_value; public B(int value) { ArgumentOutOfRangeException.ThrowIfLessThan(value, 10); p_value = value; } public void Print() => Console.WriteLine($"Value is: {p_value}"); public void Dispose() { Console.WriteLine("B disposed"); GC.SuppressFinalize(this); } } class C : IDisposable { private readonly int p_value; public C(int value) { ArgumentOutOfRangeException.ThrowIfLessThan(value, 20); p_value = value; } public void Print() => Console.WriteLine($"Value is: {p_value}"); public void Dispose() { Console.WriteLine("C disposed"); GC.SuppressFinalize(this); } } class MyClass : IDisposable { private readonly A _a; private readonly B _b; private readonly C _c; private bool _disposedValue; // 私有构造函数,强制通过工厂方法创建 private MyClass(A a, B b, C c) { _a = a ?? throw new ArgumentNullException(nameof(a)); _b = b ?? throw new ArgumentNullException(nameof(b)); _c = c ?? throw new ArgumentNullException(nameof(c)); } // 静态工厂方法(修正为静态,原代码是实例方法,不符合工厂模式) public static MyClass Create(int value) { var disposableStack = new Stack<IDisposable>(); try { // 每创建一个依赖就压入栈 var a = new A(value); disposableStack.Push(a); var b = new B(value); disposableStack.Push(b); var c = new C(value); disposableStack.Push(c); // 全部创建成功:清空栈,对象转由MyClass托管 disposableStack.Clear(); return new MyClass(a, b, c); } catch { // 清理所有已创建的对象 foreach (var disposable in disposableStack) { try { disposable.Dispose(); } catch (Exception ex) { // 可选:记录清理异常,避免因清理失败吞掉原创建异常 Console.WriteLine($"Failed to dispose {disposable.GetType().Name}: {ex.Message}"); } } // 重新抛出原异常 throw; } } // 标准Dispose模式实现 protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { // 仅在手动调用Dispose时释放托管资源(终结器调用时托管资源可能已被回收) _a.Dispose(); _b.Dispose(); _c.Dispose(); } // 释放非托管资源(如果有的话) // ... _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~MyClass() { Dispose(disposing: false); } }
关键细节说明
- 逆序清理:栈是后进先出结构,清理时会先释放最后创建的对象,这更符合资源依赖逻辑(后创建的对象可能依赖先创建的,先释放后创建的能避免引用问题)。
- 异常安全:在
catch块中重新抛出原异常,确保调用者能拿到原始错误信息;同时捕获Dispose可能抛出的异常(虽然规范不建议,但实际场景中可能存在),避免因清理失败吞掉原异常。 - 私有构造函数:强制通过工厂方法创建实例,避免直接实例化导致的资源泄漏风险。
- 标准Dispose模式:将托管资源的释放放在
disposing: true分支,避免终结器调用时访问已回收的托管对象。
三、扩展到N个依赖的场景
如果依赖数量更多(比如5个、10个),只需要在Create方法中重复“创建对象→压入栈”的逻辑即可,清理代码完全不需要修改,扩展性极强。例如新增D/E两个依赖:
public static MyClass Create(int value) { var disposableStack = new Stack<IDisposable>(); try { var a = new A(value); stack.Push(a); var b = new B(value); stack.Push(b); var c = new C(value); stack.Push(c); var d = new D(value); // 新增依赖 stack.Push(d); var e = new E(value); // 新增依赖 stack.Push(e); stack.Clear(); return new MyClass(a, b, c, d, e); // 调整构造函数参数 } catch { // 清理逻辑完全不变 foreach (var disposable in disposableStack) { try { disposable.Dispose(); } catch (Exception ex) { Console.WriteLine($"Dispose failed: {ex.Message}"); } } throw; } }
四、进一步优化:封装清理逻辑成辅助类
如果多个工厂方法都需要这种清理逻辑,可以封装一个辅助类复用代码:
public static class DisposableStackHelper { public static T CreateAndCleanupOnFailure<T>(Func<Stack<IDisposable>, T> creator) { var stack = new Stack<IDisposable>(); try { var result = creator(stack); stack.Clear(); return result; } catch { foreach (var disposable in stack) { try { disposable.Dispose(); } catch (Exception ex) { Console.WriteLine($"Dispose failed: {ex.Message}"); } } throw; } } }
然后MyClass的Create方法可以简化为:
public static MyClass Create(int value) { return DisposableStackHelper.CreateAndCleanupOnFailure(stack => { var a = new A(value); stack.Push(a); var b = new B(value); stack.Push(b); var c = new C(value); stack.Push(c); return new MyClass(a, b, c); }); }
备注:内容来源于stack exchange,提问作者Aycon
相关产品推荐
相关产品推荐

