2

好的,我已经阅读了一些关于 IDisposable 最佳实践的内容,我认为我基本上明白了(最后)。

我的问题与从 IDisposable 基类继承有关。我看到的所有示例都在子类中一遍又一遍地编写相同的代码块,但我没有看到优势。

为什么不简单地将虚拟方法烘焙到基类中,在正确的时间从(私有实现的)IDisposable 例程内部调用它,这样子类就不会那么混乱,但仍然有机会管理它们的资源?

我建议的基类:

public abstract class DreamDisposableBase : IDisposable
{
    private bool _disposed = false;


    protected virtual void LocalDispose(bool disposing)
    {   
    }

    ~DreamDisposableBase()
    { 
        // finalizer being called implies two things:
        //  1. our dispose wasn't called (because we suppress it therein)
        //  2. we don't need to worry about managed resources; they're also subject to finalization

        // so....we need to call dispose with false, meaning dispose but only worry about *unmanaged* resources:

        dispose(false);
    }


    void IDisposable.Dispose()
    {
        dispose(true);  // true argument really just means that we're invoking it explicitly
    }


    private void dispose(bool disposing)
    {
        if (!_disposed)
        {
            // give sub-classes their chance to release their resources synchronously
            LocalDispose(disposing);

            if (disposing)
            { 
                // true path is our cue to release our private heap variables...
            }

            // do stuff outside of the conditional path which *always* needs to be done - release  unmanaged resources

            // tell .net framework we're done, don't bother with our finalizer - 
            GC.SuppressFinalize(this);

            // don't come back through here
            _disposed = true;
        }
    }

}
4

3 回答 3

0

我不希望每种类型都有终结器。很少需要在终结器中执行任何工作。如果 disposing 是真的,几乎所有的实现都不会做任何事情。终结器会影响性能,因为它们会导致升级到 Gen2,需要清理两个集合,并且调用终结器是单线程的。

大多数类不包装非托管资源,如果这样做,它们应该使用SafeHandle类型或其他类型。这也使得终结器变得不必要。

于 2013-10-18T13:44:20.917 回答
0

你说:

我的问题与从 IDisposable 基类继承有关。我看到的所有示例都在子类中一遍又一遍地编写相同的代码块,但我没有看到优势。

这不是真的,在 IDisposable 模式中,方法:

protected virtual void Dispose(bool disposing)

应该被继承类覆盖。

您需要注意该Dispose(bool disposing)方法实际上是您的 LocalDisposing(bool disposing)方法。我认为,这个事实是你困惑的根源。

请阅读开创性书籍的相关部分: 框架设计指南,第二版

引用本书:

如果您从已经实现该模式的类继承,只需重写 Dispose(bool) 方法以提供额外的资源清理逻辑

在派生类中,代码如下所示:

protected override void Dispose(bool disposing)
{
  if(disposing)
  {
      //release own resources
  } 
}

另请注意,在这种情况下,您应该GC.SuppressFinalize(this)只在非虚拟 Dispose 方法中调用。同样在您的代码中,您正在隐式实现 IDisposable 接口,重要的是要意识到这一点。

于 2013-10-18T13:06:26.070 回答
0

我没有看到您的代码有任何真正的改进,但由于该模式是非标准的,其他开发人员可能更难理解它。要使用您的模式创建派生类,您需要以下内容:

class DerivedDreamDisposable : DreamDisposableBase
{
    protected override void LocalDispose(bool disposing)
    {
        if (disposing)
        {
            // Dispose aggregated objects that are disposable.
        }

        // Dispose unmanaged resources.

        _disposed = true;
        base.LocalDispose(disposing);
    }
}

使用标准IDisposable模式,您的派生类是这样的:

class DerivedDisposable : DisposableBase
{
    bool _disposed;

    protected override void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // Dispose aggregated objects that are disposable.
            }

            // Dispose unmanaged resources.

            _disposed = true;
        }
        base.Dispose(disposing);
    }
}

通过派生DreamDisposable避免复制字段以跟踪对象的已处置状态。然而,除此之外,这些方法实际上是相同的。此外,在您的基类LocalDispose中是空的,并且您已将代码移动到私有Dispose方法中,但这可以通过进行小的重构来修复。

但是,许多类不会释放任何非托管资源,并且由于调用该Dispose方法是幂等的(您可以多次调用它),您通常不必跟踪已释放的状态,并且您的一次性代码被简化为:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        _child1.Dispose();
        _child2.Dispose();
    }
}

如果您的继承层次结构中没有任何非托管资源,则不需要终结器,并且该disposing参数将始终为真。您的Dispose方法将是:

protected override void Dispose(bool disposing)
{
    _child1.Dispose();
    _child2.Dispose();
}

一般来说,避免使用终结器对性能有好处,这就是为什么你呼吁GC.SuppressFinalize撤销你通过实现终结器造成的伤害。

但在许多情况下,您仍然需要跟踪对象的释放状态,因为ObjectDisposedException如果在释放对象后调用方法,则必须抛出异常,在这种情况下,我的简化Disposed方法太简单了。这是一个关于如何在不复制_disposed每个子类中的标志并仍然使用标准处置模式的情况下处理该问题的示例:

class DisposableBase : IDisposable
{
    bool _disposed;

    ~DisposableBase()
    {
        Dispose(false);
        GC.SuppressFinalize(this);
    }

    public void Dispose()
    {
        if (_disposed)
           return;
        Dispose(true);
        _disposed = true;
    }

    public void DoStuff()
    {
        ThrowIfDisposed();
        // Now, do stuff ...
    }

    protected virtual void Dispose(bool disposing)
    {
        // Dispose resources ...
    }

    protected void ThrowIfDisposed()
    {
        if (_disposed)
            throw new ObjectDisposedException(GetType().FullName);
    }
}

任何派生类都不需要跟踪已处置状态,而是应ThrowIfDisposed在依赖于未处置对象的所有公共方法中使用。

于 2013-10-18T13:22:48.510 回答