1. ホーム
  2. c#

[解決済み] IDisposableが全クラスに広がるのを防ぐには?

2022-06-02 08:26:49

質問

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

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

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

A Bus には Driver は、その Driver は2つの Shoe があり、それぞれ Shoe には 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 の実装を使うこともできます。 Dispose(bool) とDestructorを必要としない、よりシンプルな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 というように ShoeIDisposable も必要です。

そして DriverShoe だから Driver は、必ず IDisposable . そして BusDriver だから Bus は、必ず IDisposable といった具合になります。

突然ですが、私の小さな変更で Shoelace への小さな変更が多くの作業を引き起こし、上司はなぜ私が Bus を変更するために Shoelace .

質問

どのようにしたら、この IDisposable が広がるのを防ぎつつ、アンマネージドオブジェクトが適切に廃棄されるようにするにはどうしたらよいでしょうか。

どのように解決するのですか?

IDisposableの拡散を本当に"防止"することはできません。のように、ディスポーザブルにする必要があるクラスもあります。 AutoResetEvent のように、廃棄される必要があるクラスもあり、最も効率的な方法は Dispose() メソッドで行うのが最も効率的です。しかし、このメソッドは何らかの形で呼び出されなければならないので、まさにあなたの例のように、IDisposableをカプセル化したり含むクラスはこれらを廃棄しなければならないので、同様に廃棄されなければならない、などです。それを回避する唯一の方法は

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

IDisposable はオプションのケースをサポートしているため、無視できるケースもあります。 例えば、WaitHandleは、名前付きMutexをサポートするためにIDisposableを実装しています。 名前が使用されていない場合、Disposeメソッドは何もしません。 MemoryStreamもその一例で、システムリソースを一切使わず、Disposeの実装も何もしない。アンマネージドリソースが使用されているかどうかを注意深く考えることは、有益な情報になります。また、.net ライブラリの利用可能なソースを調査したり、デコンパイラーを使用することもできます。