【发布时间】:2011-10-26 17:07:06
【问题描述】:
如果这个问题有点过于开放,请提前原谅我,但我在这里看到过类似的语言讨论帖子,所以我想我会冒险。
无论如何,我已经阅读了几个关于正确实现IDisposable 类的 MSDN 帮助页面和其他各种博客。我觉得我理解的很好,但我不得不怀疑建议的类结构是否存在缺陷:
public class DisposableBase : IDisposable
{
private bool mDisposed;
~DisposableBase()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!mDisposed)
{
if (disposing)
{
// Dispose managed resources
mManagedObject.Dispose();
}
// Dispose unmanaged resources
CloseHandle(mUnmanagedHandle);
mUnmanagedHandle = IntPtr.Zero;
mDisposed = true;
}
}
}
只要上述内容用作基类,您就可以依赖子类的实现者在必要时正确覆盖 Dispose(bool) 方法。简而言之,派生类必须确保它们从被覆盖的版本中调用基本的 Dispose(bool) 方法。否则,基类的非托管资源可能永远不会被释放,从而违背了 IDisposable 接口的主要目的。
我们都知道虚拟方法的好处,但在这种情况下,它们的设计似乎不足。事实上,我认为虚拟方法的这种特殊缺点在尝试设计可视组件和类似的基类/派生类结构时经常表现出来。
考虑以下更改,使用受保护的事件而不是受保护的虚拟方法:
public class DisposeEventArgs : EventArgs
{
public bool Disposing { get; protected set; }
public DisposeEventArgs(bool disposing)
{
Disposing = disposing;
}
}
public class DisposableBase : IDisposable
{
private bool mDisposed;
protected event EventHandler<DisposeEventArgs> Disposing;
~DisposableBase()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// This method is now private rather than protected virtual
private void Dispose(bool disposing)
{
if (!mDisposed)
{
// Allow subclasses to react to disposing event
AtDisposing(new DisposeEventArgs(disposing));
if (disposing)
{
// Dispose managed resources
mManagedObject.Dispose();
}
// Dispose unmanaged resources
CloseHandle(mUnmanagedHandle);
mUnmanagedHandle = IntPtr.Zero;
mDisposed = true;
}
}
private void AtDisposing(DisposeEventArgs args)
{
try
{
EventHandler<DisposeEventArgs> handler = Disposing;
if (handler != null) handler(this, args);
}
catch
{
}
}
}
通过这种设计,无论子类是否订阅 Disposing 事件,都会始终调用基类的 Dispose(bool) 方法。在这个修改后的设置中,我可以看到的最大缺陷是调用事件侦听器的时间没有预先确定的顺序。如果有多个继承级别,这可能会出现问题,例如SubclassA 的侦听器可能在其子 SubclassB 的侦听器之前触发。这个缺陷是否严重到足以使我修改后的设计无效?
这种设计困境让我希望有某种修饰符用于类似于virtual 的方法,但它可以确保始终调用基类的方法,即使子类覆盖了该函数。如果有更好的方法来实现这一点,我将非常感谢您的建议。
【问题讨论】:
-
我认为要求派生类调用基本实现的方法的合同的一部分是合理的,特别是对于合同相当明确的 Dispose 等方法。更好的是,构建您的解决方案,以便密封 Disposable 类,您不必为整个 Dispose(bool) 混乱而烦恼。
-
但这是一个真正的问题吗?拥有资源而不被密封(可密封)应该是罕见的。我宁愿使用单元测试。
-
使用 SafeHandle 类之一,问题已解决。
标签: c# .net idisposable