これらの簡単なクラスから始めましょう...
次のような単純なクラスのセットがあるとします。
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'。」
さて、実装してみる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(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 ライブラリの利用可能なソースを調べたり、デコンパイラを使用したりすることも参考になります。