开始与这些简单类...

我是说我有一个简单的类是这样的:

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 有两个 Shoes,每 Shoe 有一个 Shoelace.所有非常愚蠢的。

增加一个IDisposable对象鞋带

后来我决定一些操作上的 Shoelace 可能是多线程,因此我添加一个 EventWaitHandle 对于该线通信。所以 Shoelace 现在看起来是这样的:

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

实现IDisposable上鞋带

但是现在 微软的FxCop 会抱怨: "实现IDisposable上鞋带',因为它创建成员的以下IDisposable种类型:'安全访问'."

好吧,我实现的 IDisposableShoelace 和我整洁的小类变这种可怕的混乱:

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(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, 但仍然保证你不受管理的对象是妥善处置?

有帮助吗?

解决方案

你真的不能"防止"IDisposable扩散。有些课程需要设置,喜欢 AutoResetEvent, 和最有效的方式是以这样做的 Dispose() 方法,以避免的开销终结器。但是这种方法必须以某种方式,所以,正是因为在您的例的类封或包含IDisposable必须处理这些,因此他们必须一次性使用,等等。只有这样,才能避免这是:

  • 避免使用IDisposable类,如可能,锁或等待的事件,在单一的地方,保持昂贵资源的单位,等等
  • 创造他们只有当你需要他们和处置他们只是之后(该 using 图案)

在某些情况下IDisposable可以忽略,因为它支持一个可选择的情况。例如,线程实现IDisposable支持一个名为互斥。如果一个名字没有被使用,处置的方法并没有什么。流是另一个例子,它使用没有系统资源及其处置的执行也什么都不做。谨慎思考是否不受管理的资源正被用于或不可以教学。因此可以审查可用资源。净库或使用反编译器.

其他提示

在准确性方面,你不能阻止IDisposable的传播通过对象的关系,如果父对象创建和本质拥有的子对象,现在必须是一次性的。 FxCop的是在这种情况下正确的并且父母必须IDisposable的。

你可以做的是避免在您的对象层次结构添加了IDisposable到叶类。这并不总是一件容易的事,但它是一个有趣的练习。从逻辑的角度来看,没有理由认为鞋带必须是一次性的。除了在此处添加的WaitHandle的,是不是也可以在它的使用点添加鞋带和WaitHandle的之间的关联。最简单的方法是通过Dictionary实例。

如果可以将WaitHandle的经由地图在WaitHandle的实际使用,那么您可以打破这种链。该点移动到一个松散的关联

要防止IDisposable蔓延,你应该尝试来封装单个方法的内部使用的一次性的对象。尝试以不同的方式设计Shoelace

class Shoelace { 
  bool tied = false; 

  public void Tie() {

    using (var waitHandle = new AutoResetEvent(false)) {

      // you can even pass the disposable to other methods
      OtherMethod(waitHandle);

      // or hold it in a field (but FxCop will complain that your class is not disposable),
      // as long as you take control of its lifecycle
      _waitHandle = waitHandle;
      OtherMethodThatUsesTheWaitHandleFromTheField();

    } 

  }
} 

等待句柄的范围限定于Tiemethod,和类不需要有一个一次性字段,因此将不必是一次性的本身。

由于等待句柄是Shoelace内部的实现细节,它不应该以任何方式改变它的公共接口,如在其声明中添加一个新的接口。当你不需要一次性场了,你会删除IDisposable声明什么会怎样呢?如果你想想Shoelace 抽象,你很快意识到,它不应该是基础设施的依赖污染,像IDisposableIDisposable应保留类,它们的抽象封装,对于确定性的清理要求的资源;即,对于类,其中可处置是的一部分的抽象

这感觉很像一个更高层次的设计问题,因为通常情况下,当一个“速战速决”转予陷入泥潭。对于如何进行更多的讨论时,你可能会发现这个线程有帮助的。

有趣的是,如果Driver如上述所定义:

class Driver
{
    Shoe[] shoes = { new Shoe(), new Shoe() };
}

然后,当ShoeIDisposable,FxCop的(v1.36)不抱怨Driver还应当IDisposable

然而,如果它的定义如下:

class Driver
{
    Shoe leftShoe = new Shoe();
    Shoe rightShoe = new Shoe();
}

然后它会抱怨。

我怀疑这只是一个的FxCop的限制,而不是一个解决方案,因为在第一个版本仍然被由Shoe创建的Driver实例,但仍需要以某种方式设置。

我不认为这是防止IDisposable的蔓延的技术方式,如果你把你的设计,使紧密耦合。然后人们应该知道,如果设计是正确的。

在你的榜样,我觉得很有道理有自己的鞋鞋带,也许,司机应该拥有他/她的鞋。然而,公交车应该没有自己的驱动程序。通常情况下,公交车司机不按公交车的废料场:)在驱动程序和鞋的情况下,司机很少让自己的鞋子,这意味着他们并不真正“拥有”他们。

一种替代设计可以是:

class Bus
{
   IDriver busDriver = null;
   public void SetDriver(IDriver d) { busDriver = d; }
}

class Driver : IDriver
{
   IShoePair shoes = null;
   public void PutShoesOn(IShoePair p) { shoes = p; }
}

class ShoePairWithDisposableLaces : IShoePair, IDisposable
{
   Shoelace lace = new Shoelace();
}

class Shoelace : IDisposable
{
   ...
}

在新的设计是不幸的是更为复杂,因为它需要额外的类实例化和鞋子和驱动程序的具体情况处置,而是要被解决的问题这种并发症是与生俱来的。好事是,公交车不再需要一次性只是为了鞋带处置的目的。

这基本上是当你混合组合或聚合一次性班会发生什么。如前所述,第一出路将是重构的WaitHandle了鞋带。

说了这么多,可以大大地剥离下来的一次性模式时,你不必非托管资源。 (我仍然在寻找用于此的官方参考。)

但是,可以省略析构函数和GC.SuppressFinalize(本);也许清理虚拟无效的Dispose(布尔处置)位。

如何使用控制反转?

class Bus
{
    private Driver busDriver;

    public Bus(Driver busDriver)
    {
        this.busDriver = busDriver;
    }
}

class Driver
{
    private Shoe[] shoes;

    public Driver(Shoe[] shoes)
    {
        this.shoes = shoes;
    }
}

class Shoe
{
    private Shoelace lace;

    public Shoe(Shoelace lace)
    {
        this.lace = lace;
    }
}

class Shoelace
{
    bool tied;
    private AutoResetEvent waitHandle;

    public Shoelace(bool tied, AutoResetEvent waitHandle)
    {
        this.tied = tied;
        this.waitHandle = waitHandle;
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var leftShoeWaitHandle = new AutoResetEvent(false))
        using (var rightShoeWaitHandle = new AutoResetEvent(false))
        {
            var bus = new Bus(new Driver(new[] {new Shoe(new Shoelace(false, leftShoeWaitHandle)),new Shoe(new Shoelace(false, rightShoeWaitHandle))}));
        }
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top