如何防止IDisposable传播到所有类?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (17)

假设我有一组简单的类,如下所示:

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有一个DriverDriver有两个ShoeS,每个Shoe有一个Shoelace。。

添加一个IDisposable对象

稍后,我决定对Shoelace可能是多线程的,所以我添加了一个EventWaitHandle用于线程之间的通信。所以Shoelace现在看起来是这样的:

class Shoelace
{
    private AutoResetEvent waitHandle = new AutoResetEvent(false);
    bool tied = false;
    // ... other stuff ..
}

实现IDisposable

会出现:“在‘Shoelace’上实现IDisposable,因为它创建了以下IDisposable类型的成员:‘EventWaitHandle’。”

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);
    }
}

FxCop会抱怨Shoe创建Shoelace,所以Shoe一定是IDisposable

Driver创造Shoe所以Driver一定是IDisposable。和Bus创造Driver所以Bus一定是IDisposable诸若此类

突然间我小小的改变Shoelace给我带来了很多工作,我的老板想知道我为什么要退房Bus改变Shoelace.

问题

如何防止IDisposable,但仍然确保正确处理非托管对象吗?

提问于
用户回答回答于

你不能真正“阻止”IDisposable的传播。有些类需要处理,如AutoResetEvent,而最有效的方法是在Dispose()方法以避免终结器的开销。但是必须以某种方式调用此方法,正如在示例中封装或包含IDisposable的类必须释放这些类一样,因此它们也必须是一次性的,等等。避免这种情况的唯一办法是:

  • 尽可能避免使用IDisposable类,锁定或等待单个位置的事件,将昂贵的资源保存在单个位置等。
  • 只在需要时才创建它们,然后再将它们处理掉(using(模式)

在某些情况下,IDisposable可以被忽略,因为它支持可选的情况。例如,WaitHandle实现了IDisposable来支持一个命名的Mutex。如果没有使用名称,Dispose方法将不执行任何操作。MemoryStream是另一个例子,它不使用系统资源,它的Dispose实现也什么也不做。仔细考虑是否正在使用非托管资源可以起到指导性作用。也可以检查.NET库的可用源或使用反编译器。

用户回答回答于

就正确性而言,如果父对象创建并本质上拥有一个必须是一次性的子对象,则无法阻止IDisposable通过对象关系的传播。在这种情况下,FxCop是正确的,父程序必须是IDisposable。

可以避免向对象层次结构中的叶子类添加IDisposable。这并不总是一个容易的任务,但这是一个有趣的练习。从逻辑的角度来看,没有理由认为鞋带必须是一次性的。与在这里添加WaitHandle不同,是否还可以在使用的时候添加鞋带和WaitHandle之间的关联。最简单的方法是通过字典实例。

如果可以通过地图将WaitHandle移动到一个松散的关联中,而WaitHandle实际上是使用的,那么可以打破这个链。

扫码关注云+社区