こちらの回答を読んだ後、 IDisposableの実装を簡素化するために、クラスをシール済みとしてマークすることにしました。seal が IDisposable の実装に影響を与えるのはなぜですか (たとえばGC.SuppressFinalize(this);
、呼び出す必要がないなど)? 何が起こっているのか説明してください。クラスを封印した理由を仲間の開発者に説明できる必要があります。
3 に答える
を実装するクラスがIDisposable
封印されていない場合、派生クラスが に応答して何かを実行する必要がある可能性がありますDispose
が、基本クラスのアクションも実行するDispose
必要があります。Dispose
クラスが常に と同義である public メンバーを公開する場合IDisposable.Dispose
、必要なセマンティクスは、パブリック仮想Dispose
メソッドで暗黙的なインターフェイス実装を使用するだけで、C# で実現できます。
このアプローチには 2 つの問題があります。
- 親がパブリックな「Dispose」メソッドを公開する場合と公開しない場合では、派生クラスが異なるメソッドを使用する必要があります。public `Dispose` メソッドを公開していないクラスが公開している封印されていないクラスに継承されると、事態は非常に混乱する可能性があります。
- 一部のベース処理コードは、派生クラスの処理コードの前に実行する必要があり (たとえば、`Dispose` 試行の繰り返しを抑制するコード)、一部は後で実行する必要があります (例: `GC.SuppressFinalize()`)。これを実現する唯一の方法は、非仮想ラッパーで保護された仮想関数を呼び出すことです。ところで、Microsoft のラッパーは繰り返しの Dispose を適切に抑制しませんが、ラッパーはそのような抑制コードに適した唯一の場所であることに注意してください。
Dispose
Microsoftは、基本クラスがオーバーライドしないFinalize
が、派生クラスがFinalize
クリーンアップに使用する場合にそのパターンを使用することを意図しているように見えることに注意してください。それが意図されているかもしれませんが、その目的には適したパターンではありません。ごくわずかな例外を除いて、クリーンアップのためにのみオーバーライドする必要があるクラスは、 のFinalize
ような簡単なクラスから派生したクラスだけですObject
。クラスが を実装しているIDisposable
がオーバーライドしていない場合Finalize
、派生クラスがオーバーライドすべき唯一の目的は、呼び出されFinalize
た場合にアラームを鳴らすことFinalize
であり、その使用法についても議論の余地があります (より良いパターンは次のようになります:
クラスは何でも:IDisposable { IDisposable DisposedStatusObject; // センチネル値として使用できる静的ダミー オブジェクト インスタンスを生成します // `IDisposable` である必要がありますが、実際にリソースを保持するべきではありません。 static IDisposable DisposedStatusDisposed = new List<int>().GetEnumerator(); public bool Disposed {get {return (DisposedStatusObject == DisposedStatusDisposed);} } なんでもいい() { DisposedStatusObject = 新しい DisposalAlarm(); // コンストラクタで最初に行うこと } ボイドディスポーズ() { IDisposable prevStatus; prevStatus = Interlocked.Exchange(DisposedStatus, DisposedStatusDisposed); if (prevStatus != DisposedStatusDisposed) { 破棄 (真); prevStatus.Dispose(); } } }
クラスは、メソッドが最初に呼び出されずにメソッドが呼び出された場合にアラームを鳴らすDisposalAlarm()
オーバーライドされたメソッドを持つクラスであると見なされます。のメソッドは、派生クラスのメソッドが適切に返された場合、アラームがキャンセルされることを保証します。のインスタンスに抑制されていないファイナライザーがある場合、そのファイナライザーが実行されるか抑制されるまで、直接または間接的な参照を保持するすべてのものを保持する必要があることに注意してください。対照的に、オブジェクトを追加しても、 の寿命は延長されません。Finalize()
Finalize()
Dispose()
Dispose
whatever
whatever
whatever
DisposalAlarm
whatever
封印されたクラスは基本クラスとして使用されることを意図していませんが、封印されていないクラスは使用されます。したがって、違いがあります。封印されていないクラスは、派生クラスDispose()
が独自に実装する方法を提供する必要がありますが、封印されたクラスは拡張できないため、この責任はありません。
クラスを作成するsealed
ということは、そこから派生したクラスが存在できないことを意味します。つまり、 の実装でIDisposable
は、派生クラスの動作 (または誤動作) を考慮する必要はありません。