只要层次结构中的所有 类 仅使用托管资源,就可以使用虚拟 Dispose() 方法吗?

Is it OK to have a virtual Dispose() method as long as all classes in the hierarchy only use managed resources?

我知道实施 dispose pattern 的一般指南警告不要实施虚拟 Dispose()。然而,大多数情况下我们只使用 class 中的托管资源,因此完整的处置模式似乎有点矫枉过正——即我们不需要终结器。在这种情况下,在基础 class 中有一个虚拟 Dispose() 可以吗?

考虑这个简单的例子:

abstract class Base : IDisposable
{
    private bool disposed = false;
    public SecureString Password { get; set; } // SecureString implements IDisposable.

    public virtual void Dispose()
    {
        if (disposed)
            return;

        if (Password != null)       
            Password.Dispose();     

        disposed = true;
    }
}

class Sub : Base
{
    private bool disposed = false;
    public NetworkStream NStream { get; set; } // NetworkStream implements IDisposable.

    public override void Dispose()
    {   
        if (disposed)
            return;

        if (NStream != null)
            NStream.Dispose();

        disposed = true;
        base.Dispose();
    }
}

我发现这比完整的处置模式更具可读性。我知道如果我们在 Base class 中引入非托管资源,这个例子就会出现问题。但假设这不会发生。那么上面的例子是valid/safe/robust,还是有什么问题?我是否应该坚持标准的成熟处理模式,即使没有使用非托管资源?还是上述方法在这种情况下确实完全可行 - 根据我的经验,这比处理非托管资源更常见?

在我不处理非托管资源的情况下,我已经放弃了成熟的 IDisposable 模式。

我不明白为什么你不能放弃模式并使 Dispose 虚拟 如果 你可以确保派生 classes不会引入非托管资源!

(这里有一个问题,因为你不能强制执行这个。你无法控制派生的class将添加哪些字段,所以没有绝对的使用虚拟 Dispose 的安全性。但是即使你使用了成熟的模式,派生的 class 也可能会出错,所以总会有一些信任涉及子类型遵守某些规则。)

首先,在我们只处理托管对象的情况下,拥有终结器将毫无意义:如果从终结器调用 Dispose(根据成熟模式,Dispose(disposing: false)),我们可能无法安全地 access/dereference 其他引用类型的托管对象,因为它们可能已经不存在了。只能安全地访问从正在完成的对象可达的值类型对象。

如果finalizer没有意义,那么有disposing标志也是没有意义的,用来区分Dispose是显式调用还是被finalizer调用

如果没有finalizer,也不需要GC.SuppressFinalize

我什至不确定 Dispose 在仅限托管的情况下是否仍然必须不抛出任何异常。据我所知,这条规则的存在主要是为了保护终结器线程。(请参阅@usr 的 。)

如您所见,一旦终结器消失,classic Disposable 模式的大部分就不再需要了。

I understand that this example would become problematic if we introduced an unmanaged resource into the Base class. But suppose that this won't happen. Is the above example then valid/safe/robust, or does it pose any problem? Should I stick to the standard full-blown dispose pattern, even if no unmanaged resources are used?

尽管您的基础 class 仅使用托管资源(这在未来不会改变),但您不能保证派生的 class 不会使用非托管资源。因此,即使您总是调用 Dispose(bool) 方法,也请考虑在您的基础 class 中实现 Basic Dispose Pattern(具有虚拟 Dispose(bool) 但没有终结器的 class) true.

当某天从您的 Base 派生出一个新的子 class 时,它使用非托管资源,它的终结器可能想用 false 调用 Dispose(bool) .当然,你可以在派生的 class:

中引入一个新的 Dispose(bool)
public class SubUnmanaged : Base
{
    IntPtr someNativeHandle;
    IDisposable someManagedResource;

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

    ~SubUnmanaged();
    {
        base.Dispose();
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (someNativeHandle != IntPtr.Zero)
            Free(someNativeHandle);

        if (disposing)
           someManagedResource.Dispose();
    }
}

但我认为这有点令人讨厌。通过封装原来的virtualDispose()方法,可以防止后面的继承者因为多个virtualDispose方法而混淆,并且可以从finalizer和密封Dispose()。您必须在每个使用非托管资源并派生自 Base 或完全托管的子 class 的子 class 中执行相同的解决方法。但这已经与模式相去甚远,总是让事情变得棘手。