139

これらの簡単なクラスから始めてください...

次のような単純なクラスのセットがあるとしましょう。

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

ABusには、がありDriver、にDriverは2つShoeのsがあり、それぞれShoeに。がありShoelaceます。すべて非常にばかげています。

IDisposableオブジェクトをShoelaceに追加します

後で、の一部の操作をShoelaceマルチスレッド化できると判断したのでEventWaitHandle、スレッドが通信するためのを追加します。だからShoelace今は次のようになります:

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

靴紐にIDisposableを実装する

しかし、MicrosoftのFxCopは、「次のIDisposableタイプのメンバーを作成するため、「Shoelace」にIDisposableを実装します:「EventWaitHandle」」と文句を言います。

さて、私は実装IDisposableします、Shoelaceそして私のきちんとした小さなクラスはこの恐ろしい混乱になります:

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を作成します。DriverBusIDisposable

突然、への小さな変更が多くの​​作業を引き起こし、上司は、に変更を加えるためShoelaceになぜチェックアウトする必要があるのか​​疑問に思っています。BusShoelace

質問

この拡散をどのように防止しますIDisposableが、それでも管理されていないオブジェクトが適切に廃棄されるようにしますか?

4

7 に答える 7

36

IDisposableの拡散を実際に「防ぐ」ことはできません。のように、いくつかのクラスを破棄する必要がありますAutoResetEvent。最も効率的な方法はDispose()、ファイナライザーのオーバーヘッドを回避するためにメソッドで破棄することです。ただし、このメソッドはなんらかの方法で呼び出す必要があります。例のように、IDisposableをカプセル化または含むクラスはこれらを破棄する必要があるため、使い捨てにする必要があります。これを回避する唯一の方法は次のとおりです。

  • 可能な場合はIDisposableクラスの使用を避け、単一の場所でイベントをロックまたは待機し、高価なリソースを単一の場所に保持するなど。
  • 必要なときにだけ作成し、直後に破棄します(usingパターン)

IDisposableはオプションのケースをサポートしているため、無視できる場合があります。たとえば、WaitHandleはIDisposableを実装して、名前付きMutexをサポートします。名前が使用されていない場合、Disposeメソッドは何もしません。MemoryStreamは別の例であり、システムリソースを使用せず、Dispose実装も何もしません。管理されていないリソースが使用されているかどうかを慎重に検討することは、有益な場合があります。したがって、.netライブラリで利用可能なソースを調べたり、逆コンパイラを使用したりできます。

于 2009-03-19T11:21:54.270 に答える
20

正確さに関しては、親オブジェクトが作成し、現在は破棄する必要がある子オブジェクトを本質的に所有している場合、オブジェクトの関係を通じて IDisposable の拡散を防ぐことはできません。この状況では FxCop が適切であり、親は IDisposable である必要があります。

できることは、オブジェクト階層のリーフ クラスに IDisposable を追加しないようにすることです。これは必ずしも簡単な作業ではありませんが、興味深い演習です。論理的な観点から言えば、ShoeLace が使い捨てである必要がある理由はありません。ここで WaitHandle を追加する代わりに、ShoeLace と WaitHandle の間の関連付けを、それが使用されている時点で追加することもできますか。最も簡単な方法は、 Dictionary インスタンスを使用することです。

WaitHandle が実際に使用される時点で、マップを介して WaitHandle を緩やかな関連付けに移動できる場合は、この連鎖を断ち切ることができます。

于 2009-03-19T12:23:28.023 に答える
16

拡散を防ぐ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();

    } 

  }
} 

待機ハンドルのスコープはメソッドに限定されてTieおり、クラスは使い捨てフィールドを持つ必要がないため、それ自体を使い捨てにする必要はありません。

待機ハンドルは 内の実装の詳細であるためShoelace、その宣言に新しいインターフェイスを追加するなど、パブリック インターフェイスを変更しないでください。使い捨てフィールドが不要になった場合、IDisposable宣言を削除しますか? Shoelace 抽象化について考えると、 のようなインフラストラクチャの依存関係によって汚染されるべきではないことがすぐにわかりますIDisposableIDisposable確定的なクリーンアップを必要とするリソースをカプセル化する抽象化を持つクラス用に予約する必要があります。つまり、廃棄可能性が抽象化の一部であるクラスの場合です。

于 2010-06-09T12:48:27.343 に答える
3

設計を緊密に結合したままにしておくと、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
{
   ...
}

靴とドライバーの具体的なインスタンスをインスタンス化して破棄するために追加のクラスが必要になるため、残念ながら新しい設計はより複雑になりますが、この複雑さは解決しようとしている問題に固有のものです。良いことは、靴ひもを処分するためだけにバスを使い捨てにする必要がなくなったことです。

于 2010-01-06T10:39:29.957 に答える
3

興味深いことに、Driverが上記のように定義されている場合:

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

すると、FxCop (v1.36)ShoeIDisposable文句を言わないDriverはずIDisposableです。

ただし、次のように定義されている場合:

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

それからそれは文句を言うでしょう。

これは解決策ではなく、FxCop の単なる制限であると思われます。最初のバージョンでは、Shoeインスタンスがまだ によって作成されておりDriver、何らかの方法で破棄する必要があるためです。

于 2009-03-19T13:39:52.830 に答える
3

これは基本的に、Composition または Aggregation と Disposable クラスを混在させたときに起こることです。前述のように、最初の解決策は、靴紐から waitHandle をリファクタリングすることです。

そうは言っても、管理されていないリソースがない場合は、Disposable パターンを大幅に取り除くことができます。(私はまだこれについての公式のリファレンスを探しています。)

ただし、デストラクタと GC.SuppressFinalize(this); は省略できます。仮想ボイドDispose(bool disposing)を少しクリーンアップするかもしれません。

于 2009-03-19T11:55:54.593 に答える
1

制御の反転を使用するのはどうですか?

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))}));
        }
    }
}
于 2014-07-02T11:34:55.960 に答える