IDisposable がすべてのクラスに広がるのを防ぐにはどうすればよいでしょうか? 質問する

IDisposable がすべてのクラスに広がるのを防ぐにはどうすればよいでしょうか? 質問する

これらの簡単なクラスから始めましょう...

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

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、それぞれにShoeがありますShoelace。すべて非常にばかげています。

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

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

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

靴ひもにIDisposableを実装する

でも今マイクロソフトの FxCop文句を言うだろう:「'Shoelace' に IDisposable を実装します。これは、次の IDisposable 型のメンバーを作成するためです: 'EventWaitHandle'。」

さて、実装してみる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)およびデストラクタを必要とせずに、より単純な dispose 実装を使用することもできます。

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ながら、管理されていないオブジェクトが適切に破棄されることをどのように保証するのでしょうか?

ベストアンサー1

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

  • 可能な限り IDisposable クラスの使用を避け、イベントを 1 か所でロックまたは待機し、高価なリソースを 1 か所に保持するなどします。
  • using必要なときだけ作成し、(パターン)が終わったらすぐに破棄する

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

おすすめ記事