1

我永远记不起实现 IDisposable 接口的所有规则,所以我试图想出一个基类来处理所有这些并使 IDisposable 易于实现。我只是想听听你的意见,如果这个实现是好的,或者你是否看到了我可以改进的地方。这个基类的用户应该从它派生,然后实现两个抽象方法ReleaseManagedResources()ReleaseUnmanagedResources(). 所以这里是代码:

public abstract class Disposable : IDisposable
{
    private bool _isDisposed;
    private readonly object _disposeLock = new object();

    /// <summary>
    /// called by users of this class to free managed and unmanaged resources
    /// </summary>
    public void Dispose() {
        DisposeManagedAndUnmanagedResources();
    }

    /// <summary>
    /// finalizer is called by garbage collector to free unmanaged resources
    /// </summary>
    ~Disposable() { //finalizer of derived class will automatically call it's base finalizer
        DisposeUnmanagedResources();
    }

    private void DisposeManagedAndUnmanagedResources() {
        lock (_disposeLock) //make thread-safe
            if (!_isDisposed) { //make sure only called once
                try { //suppress exceptions
                    ReleaseManagedResources();
                    ReleaseUnmanagedResources();
                }
                finally {
                    GC.SuppressFinalize(this); //remove from finalization queue since cleaup already done, so it's not necessary the garbage collector to call Finalize() anymore
                    _isDisposed = true;
                }
            }
    }

    private void DisposeUnmanagedResources() {
        lock (_disposeLock) //make thread-safe since at least the finalizer runs in a different thread
            if (!_isDisposed) { //make sure only called once
                try { //suppress exceptions
                    ReleaseUnmanagedResources();
                }
                finally {
                    _isDisposed = true;
                }
            }
    }

    protected abstract void ReleaseManagedResources();
    protected abstract void ReleaseUnmanagedResources();

}
4

4 回答 4

12

我无法真正评论您的代码的正确性,但我怀疑您是否会发现在继承树的基础上拥有 Disposable 类尽可能灵活。当你想从你不拥有的东西继承时,它不会有太大用处。我认为 IDispoasble 被保留为一个接口,因为它可能在许多不同的域中使用。在继承层次结构的底部提供一个具体的实现将是......限制。

于 2009-05-17T14:57:51.963 回答
2

编辑是的,我误读了关于分离处置托管和非托管资源的部分(仍然在第一杯咖啡上)。

然而,锁仍然几乎可以肯定是没有必要的。是的,在被动处置期间,代码将在终结器线程上运行,这与它最初运行的线程不同。但是,如果对象以这种方式完成,CLR 已经确定没有对该对象的现有引用并因此收集它。因此,当时没有其他地方可以调用您的处置,因此没有理由锁定。

一些其他风格的评论。

为什么要使方法抽象?通过这样做,您将强制派生类实现处置托管和非托管资源的方法,即使它们没有任何可处置的资源。确实,如果您没有任何东西要处理,那么从此类派生是没有意义的。但我认为只有一个或另一个而不是两者都是相当普遍的。我会让它们变得虚拟与抽象。

您还可以防止双重处置,但您不会警告开发人员他们正在双重处置对象。我意识到 MSDN 文档说双重处置本质上应该是无操作的,但同时在什么情况下这应该是合法的?一般来说,在对象被释放后访问它是个坏主意。双重处置需要重复使用已处置的对象,并且可能是一个错误(如果在主动处置中未抑制最终确定,则可能会发生这种情况,但这也是一个坏主意)。如果我正在实现这个,我会抛出双重处置来警告开发人员他们错误地使用了一个对象(即在它已经被处置之后使用它)。

于 2009-05-17T14:55:48.427 回答
2

您正在终结器中访问托管对象 _disposeLock。到那时它可能已经被垃圾收集了。不确定这会产生什么影响,因为您仅使用它来锁定。

线程安全似乎有点矫枉过正。我认为您不必担心 GC 线程和应用程序线程之间的争用。

否则它看起来是正确的。

于 2009-05-17T15:09:02.577 回答
0

如果您担心线程安全,那么我会使用轻量级联锁操作而不是重量级锁:

class MyClass: IDispose {
  int disposed = 0;

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

  public virtual void Dispose(bool disposing)
  {
     if (0 == Thread.InterlockedCompareExchange(ref disposed, 1, 0))
     {
       if (disposing)
       {
          // release managed resources here
       }
       // release unmanaged resources here
     }
  }

  ~MyClass()
  {
    Dispose(false);
  }
} 
于 2009-05-17T16:31:13.930 回答