只要层次结构中的所有 类 仅使用托管资源,就可以使用虚拟 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 中执行相同的解决方法。但这已经与模式相去甚远,总是让事情变得棘手。
我知道实施 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
我什至不确定 (请参阅@usr 的 Dispose
在仅限托管的情况下是否仍然必须不抛出任何异常。据我所知,这条规则的存在主要是为了保护终结器线程。
如您所见,一旦终结器消失,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 中执行相同的解决方法。但这已经与模式相去甚远,总是让事情变得棘手。