C#中实现IDisposable的类如何在方法内检测对象是否已释放?
现有方案存在的问题
- 同步方法强制包装为异步返回,增加了不必要的状态机开销,同时改变了方法原本的同步语义,调用方需要额外加
await,不符合设计规范 - 每个方法都要包裹lambda委托,增加了编码冗余和委托调用的性能开销,有返回值的方法还需要额外加对应重载,扩展性差
- 未处理多线程安全问题:
_isDisposed字段没有volatile修饰,多线程场景下可能出现指令重排,导致释放状态无法及时同步到其他线程,出现漏检的问题
更优的实现方案
方案1:独立抛出检测方法(最通用、性价比最高)
单独封装无开销的检测方法,所有方法开头直接调用即可,不需要委托包裹,性能最优,写法最简洁:
// 加volatile保证多线程下的状态可见性 private volatile bool _isDisposed; private void ThrowIfDisposed() { if (_isDisposed) throw new ObjectDisposedException(GetType().Name); }
如果需要更严格的多线程释放保证,可以修改Dispose方法用原子操作,确保释放逻辑只执行一次:
protected virtual void Dispose(bool disposing) { if (Interlocked.CompareExchange(ref _isDisposed, true, false) == false) { if (disposing) { _socket.Dispose(); } } }
业务方法直接调用即可,不需要修改原方法的同步/异步签名:
public void SetBuffer(int receiveBufferSize, int sendBufferSize) { ThrowIfDisposed(); _socket.Options.SetBuffer(receiveBufferSize, sendBufferSize); } // 异步方法写法完全一致 public async Task SendAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken) { ThrowIfDisposed(); await _socket.SendAsync(buffer, WebSocketMessageType.Binary, true, cancellationToken); }
如果你使用的是.NET 7及以上版本,可以直接用官方优化好的静态方法,不需要自己封装ThrowIfDisposed:
public void SetBuffer(int receiveBufferSize, int sendBufferSize) { ObjectDisposedException.ThrowIf(_isDisposed, this); _socket.Options.SetBuffer(receiveBufferSize, sendBufferSize); }
方案2:AOP自动注入(适合方法量大的场景)
如果类中的方法数量非常多,不想每个方法都手动加检测代码,可以用编译时AOP框架自动注入检测逻辑:
- 用Fody的MethodDecorator插件,自定义
[CheckDisposed]特性,标记到类或者方法上,编译时会自动在方法执行前插入释放检测代码 - 运行时无额外反射开销,和手写代码性能一致,减少重复编码
方案3:保留委托封装的修正写法
如果你确实需要保留委托封装的写法,需要修正同步重载的签名,不要返回Task:
// 同步方法重载返回void,不要加async public void CallWithCheckDispose(Action func) { ThrowIfDisposed(); func.Invoke(); } // 异步方法重载保留 public async Task CallWithCheckDispose(Func<Task> func) { ThrowIfDisposed(); await func.Invoke(); } // 有返回值的场景补充对应重载即可 public T CallWithCheckDispose<T>(Func<T> func) { ThrowIfDisposed(); return func.Invoke(); }
这种写法适合需要统一在执行前后加额外逻辑的场景,如果只需要释放检测,不如直接调用ThrowIfDisposed简洁。
内容的提问来源于stack exchange,提问作者nullrefexc
相关产品推荐
相关产品推荐

