如何更好地实现.NET IDisposable类?

Jer*_*emy 4 .net c# idisposable

如果这个问题有点过于开放,请提前原谅我,但我在这里看过类似的语言讨论帖,所以我想我会冒险尝试.

无论如何,我已经阅读了几个关于正确实现IDisposable类的MSDN帮助页面和其他各种博客.我觉得我很了解事情,但我不得不怀疑建议的类结构是否存在缺陷:

public class DisposableBase : IDisposable
{
    private bool mDisposed;

    ~DisposableBase()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!mDisposed)
        {
            if (disposing)
            {
                // Dispose managed resources
                mManagedObject.Dispose();
            }

            // Dispose unmanaged resources
            CloseHandle(mUnmanagedHandle);
            mUnmanagedHandle = IntPtr.Zero;

            mDisposed = true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

无论何时上述应该作为基类,您依赖子类的实现者在必要时正确覆盖Dispose(bool)方法.简而言之,派生类必须确保它们在其重写版本中调用基本Dispose(bool)方法.如果没有,基类的非托管资源可能永远不会被释放,从而破坏了IDisposable接口的主要目的.

我们都知道虚拟方法的好处,但似乎在这种情况下它们的设计不足.事实上,我认为虚拟方法的这个特殊缺点在尝试设计可视组件和类似的基础/派生类结构时经常表现出来.

使用受保护事件而不是受保护的虚拟方法,请考虑以下更改:

public class DisposeEventArgs : EventArgs
{
    public bool Disposing { get; protected set; }

    public DisposeEventArgs(bool disposing)
    {
        Disposing = disposing;
    }
}

public class DisposableBase : IDisposable
{
    private bool mDisposed;

    protected event EventHandler<DisposeEventArgs> Disposing;

    ~DisposableBase()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // This method is now private rather than protected virtual
    private void Dispose(bool disposing)
    {
        if (!mDisposed)
        {
            // Allow subclasses to react to disposing event
            AtDisposing(new DisposeEventArgs(disposing));

            if (disposing)
            {
                // Dispose managed resources
                mManagedObject.Dispose();
            }

            // Dispose unmanaged resources
            CloseHandle(mUnmanagedHandle);
            mUnmanagedHandle = IntPtr.Zero;

            mDisposed = true;
        }
    }

    private void AtDisposing(DisposeEventArgs args)
    {
        try
        {
            EventHandler<DisposeEventArgs> handler = Disposing;
            if (handler != null) handler(this, args);
        }
        catch
        {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此设计,无论子类是否订阅Disposing事件,都将始终调用基类'Dispose(bool)方法.我可以通过这个修改过的设置看到的最大缺陷是,在调用事件监听器时没有预定的顺序.如果存在多个级别的继承,则可能会出现问题,例如SubclassA的侦听器可能在其子子类SubclassB的侦听器之前被触发.这个缺陷是否足以使我的修改设计无效?

这种设计困境让我希望有类似的方法的某种修饰符,virtual但它确保始终调用基类的方法,即使子类覆盖了该函数.如果有更好的方法来实现这一目标,我将非常感谢您的建议.

Jar*_*Par 5

event当你真的要使用类似的继承机制时,你正在使用这里virtual.对于这样的场景,我想确保总是调用我的实现,但是想要允许基类自定义,我使用以下模式

private void Dispose(bool disposing)
  if (mDisposed) { 
    return;
  }

  if (disposing) {
    mManagedObject.Dispose();
  }

  // Dispose unmanaged resources
  CloseHandle(mUnmanagedHandle);
  mUnmanagedHandle = IntPtr.Zero;
  mDisposed = true;

  DisposeCore(disposing);
}

protected virtual void DisposeCore(bool disposing) {
  // Do nothing by default
}
Run Code Online (Sandbox Code Playgroud)

使用这种模式,我确保Dispose始终会调用我的基类实现.只需忘记调用基本方法,派生类就无法阻止我.他们仍然可以通过覆盖来选择处置模式,DisposeCore但他们不能破坏基类合同.