【发布时间】:2010-10-14 07:01:04
【问题描述】:
从这些简单的类开始...
假设我有这样一组简单的类:
class Bus
{
Driver busDriver = new Driver();
}
class Driver
{
Shoe[] shoes = { new Shoe(), new Shoe() };
}
class Shoe
{
Shoelace lace = new Shoelace();
}
class Shoelace
{
bool tied = false;
}
一个Bus 有一个Driver,Driver 有两个Shoe,每个Shoe 有一个Shoelace。都非常傻。
将 IDisposable 对象添加到鞋带
后来我决定Shoelace 上的某些操作可以是多线程的,所以我添加了一个EventWaitHandle 以供线程进行通信。所以Shoelace 现在看起来像这样:
class Shoelace
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
// ... other stuff ..
}
在鞋带上实现 IDisposable
但现在Microsoft's FxCop 会抱怨:“在 'Shoelace' 上实现 IDisposable,因为它会创建以下 IDisposable 类型的成员:'EventWaitHandle'。”
好的,我在Shoelace 上实现了IDisposable,我整洁的小班变成了这个可怕的烂摊子:
class Shoelace : IDisposable
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Shoelace()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (waitHandle != null)
{
waitHandle.Close();
waitHandle = null;
}
}
// No unmanaged resources to release otherwise they'd go here.
}
disposed = true;
}
}
或者(正如评论者所指出的)因为 Shoelace 本身没有非托管资源,我可能会使用更简单的 dispose 实现而不需要 Dispose(bool) 和析构函数:
class Shoelace : IDisposable
{
private AutoResetEvent waitHandle = new AutoResetEvent(false);
bool tied = false;
public void Dispose()
{
if (waitHandle != null)
{
waitHandle.Close();
waitHandle = null;
}
GC.SuppressFinalize(this);
}
}
在 IDisposable 传播时惊恐地看着
没错,就是这样。但是现在 FxCop 会抱怨Shoe 创建了一个Shoelace,所以Shoe 也必须是IDisposable。
而Driver 创建Shoe 所以Driver 必须是IDisposable。
而Bus 创建Driver 所以Bus 必须是IDisposable 等等。
突然间,我对Shoelace 的小改动给我带来了很多工作,我的老板想知道为什么我需要结帐Bus 才能更改为Shoelace。
问题
您如何防止IDisposable 的这种传播,但仍确保您的非托管对象得到正确处置?
【问题讨论】:
-
一个非常好的问题,我相信答案是尽量减少它们的使用并尝试保持高级 IDisposables 的短暂使用,但这并不总是可能的(尤其是那些 IDisposables 是由于互操作使用 C++ dll 或类似文件)。看看答案。
-
好的,丹,我已经更新了问题以显示在鞋带上实现 IDisposable 的两种方法。
-
我通常对依赖其他类的实现细节来保护我持谨慎态度。如果我能轻易地阻止它,那么冒险是没有意义的。也许我过于谨慎,或者我作为 C 程序员的时间太长了,但我宁愿采用爱尔兰的方法:“确定,确定”:)
-
@Dan:仍然需要进行空检查以确保对象本身没有被设置为空,在这种情况下调用 waitHandle.Dispose() 将抛出 NullReferenceException。
-
无论如何,您实际上仍应使用“在鞋带上实现 IDisposable”部分中所示的 Dispose(bool) 方法,因为它(减去终结器)是完整模式。仅仅因为一个类实现了 IDisposable 并不意味着它需要一个终结器。
标签: c# .net garbage-collection dispose idisposable