You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 00:06:02