只要层次结构中的所有类都只使用托管资源,就可以使用虚拟Dispose()方法吗
本文关键字:可以使 虚拟 Dispose 方法 资源 层次结构 | 更新日期: 2023-09-27 18:10:48
我知道实现dispose模式的一般准则警告不要实现虚拟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();
}
}
我发现这比一个完整的处置模式更可读。我知道,如果我们在Base
类中引入了一个非托管资源,那么这个例子就会出现问题。但假设这不会发生。那么,上面的例子是否有效/安全/稳健,或者它是否会带来任何问题?即使不使用非托管资源,我是否应该坚持标准的全面处置模式?或者,在这种情况下,上述方法确实完全可以吗?根据我的经验,这种情况比处理非托管资源更常见?
在不处理非托管资源的情况下,我已经放弃了全面的IDisposable
模式。
如果可以确保派生类不会引入非托管资源,那么我看不出有任何理由不能放弃该模式并使Dispose
虚拟化!
(这里存在一个问题,因为不能强制执行这一点。你无法控制派生类将添加哪些字段,因此虚拟Dispose
没有绝对的安全性。但是,即使你使用了全面的模式,派生类也可能会出错,所以总是有一些信任涉及到子类型遵守某些规则。(
首先,在我们只处理托管对象的情况下,拥有终结器是毫无意义的:如果从终结器调用Dispose
(根据全面模式,Dispose(disposing: false)
(,我们可能无法安全地访问/取消引用其他引用类型的托管对象,因为它们可能已经不在了。只有从正在完成的对象可访问的值类型对象才能安全访问。
如果终结器没有意义,那么使用disposing
标志也没有意义,该标志用于区分Dispose
是显式调用的还是终结器调用的。
如果没有终结器,也没有必要执行GC.SuppressFinalize
。
我甚至不确定(请参阅下面的@usr评论。(Dispose
在仅受管理的场景中是否仍然必须不抛出任何异常。AFAIK这个规则的存在主要是为了保护终结器线程
正如您所看到的,一旦终结器结束,许多经典的一次性模式就不再必要了。
我知道,如果我们在基类中引入了一个非托管资源,这个例子会有问题。但假设这不会发生。那么,上面的例子是否有效/安全/稳健,或者它是否会带来任何问题?即使不使用非托管资源,我是否应该坚持标准的全面处置模式?
尽管基类只使用托管资源(这种情况在将来不会改变(,但不能保证派生类不会使用非托管资源。因此,考虑在基类(一个具有虚拟Dispose(bool)
但没有终结器的类(中实现基本Dispose模式,即使您总是使用true
调用Dispose(bool)
方法。
当某一天从使用非托管资源的Base
派生出一个新的子类时,其终结器可能希望用false
调用Dispose(bool)
。当然,您可以在派生类中引入一个新的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
方法而混淆进一步的继承者,并且可以从终结器和密封的Dispose()
调用公共base.Dispose()
。您必须在每个子类中执行相同的解决方法,这些子类使用非托管资源,并且派生自Base
或完全托管的子类。但这已经与模式相去甚远,这总是让事情变得困难。