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

时间:2015-09-02 13:21:37

标签: c# .net dispose

我知道实施dispose pattern的一般准则警告不要实施虚拟public final static。但是,大多数情况下,我们只使用类中的托管资源,因此完整的部署模式似乎有点过分 - 即我们不需要终结器。在这种情况下,可以在基类中使用虚拟Dispose()吗?

考虑这个简单的例子:

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(); } } 类,这个例子就会出问题。但是假设这不会发生。以上示例是有效/安全/健壮的,还是会造成任何问题?即使没有使用非托管资源,我是否应该坚持标准的完整配置模式?或者上述方法在这种情况下确实完全正常 - 根据我的经验,这比处理非托管资源更常见?

2 个答案:

答案 0 :(得分:5)

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

我没有看到任何理由您不能放弃模式并使Dispose虚拟如果您可以确保派生类不会引入非托管资源!

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

首先,在我们只处理托管对象的情况下,使用终结器是没有意义的:如果根据完整模式从终结器(Dispose)调用Dispose(disposing: false),我们可以不安全地访问/取消引用其他引用类型的托管对象,因为它们可能已经消失。只能安全地访问从最终确定的对象可到达的值类型对象。

如果终结器没有意义,那么使用disposing标志也是没有意义的,该标志用于区分Dispose是否被显式调用或是否被终结器调用。

如果没有终结器,也没必要GC.SuppressFinalize

我甚至不确定Dispose是否仍然必须在仅托管方案中抛出任何异常。 AFAIK这个规则主要用于保护终结器线程。(参见@ usr的comment below。)

正如您所看到的,一旦终结器出现,就不再需要大部分经典的Disposable模式。

答案 1 :(得分:2)

  

我知道如果我们将非托管资源引入Base类,这个示例将会成为问题。但是假设这不会发生。以上示例是有效/安全/健壮的,还是会造成任何问题?即使没有使用非托管资源,我是否应该坚持标准的完整配置模式?

虽然您的基类使用专门的托管资源(并且将来不会更改),但您无法保证派生类不会使用非托管资源。因此,考虑在您的基类(具有虚拟Dispose(bool)但没有终结器的类)中实现Basic Dispose Pattern,即使您始终使用Dispose(bool)调用true方法。< / p>

某天某个新的子类将来自您使用非托管资源的Base,其终结器可能希望使用Dispose(bool)调用false。当然,您可以在派生类中引入新的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();
    }
}

但我认为这有点令人讨厌。通过密封原始虚拟Dispose()方法,您可以防止由于多个虚拟Dispose方法而导致进一步的继承者混淆,并且您可以从终结器调用公共base.Dispose()和密封Dispose()。您必须在每个子类中执行相同的解决方法,这些子类使用非托管资源,并派生自Base或完全托管的子类。但这已经远远不是模式,这总是让事情变得困难。