我知道实施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();
}
}
类,这个例子就会出问题。但是假设这不会发生。以上示例是有效/安全/健壮的,还是会造成任何问题?即使没有使用非托管资源,我是否应该坚持标准的完整配置模式?或者上述方法在这种情况下确实完全正常 - 根据我的经验,这比处理非托管资源更常见?
答案 0 :(得分:5)
在我没有处理非托管资源的情况下,我放弃了成熟的IDisposable
模式。
我没有看到任何理由您不能放弃模式并使Dispose
虚拟如果您可以确保派生类不会引入非托管资源!
(这里存在一个问题,因为无法强制执行此操作。您无法控制派生类将添加哪些字段,因此虚拟Dispose
没有绝对的安全性但是,即使你使用了完整的模式,派生类也可能会出错,所以总有一些信任涉及子类型遵守某些规则。)
首先,在我们只处理托管对象的情况下,使用终结器是没有意义的:如果根据完整模式从终结器(Dispose
)调用Dispose(disposing: false)
,我们可以不安全地访问/取消引用其他引用类型的托管对象,因为它们可能已经消失。只能安全地访问从最终确定的对象可到达的值类型对象。
如果终结器没有意义,那么使用disposing
标志也是没有意义的,该标志用于区分Dispose
是否被显式调用或是否被终结器调用。
如果没有终结器,也没必要GC.SuppressFinalize
。
我甚至不确定(参见@ usr的comment below。)Dispose
是否仍然必须在仅托管方案中抛出任何异常。 AFAIK这个规则主要用于保护终结器线程。
正如您所看到的,一旦终结器出现,就不再需要大部分经典的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
或完全托管的子类。但这已经远远不是模式,这总是让事情变得困难。