C# の IDisposable の実装は、Dispose() だけを考えればシンプルです。
が、少し凝ったことをしようとすると、クラスにアンマネージドなリソースを抱えざるを得ないこともあります。そのような場合には、ファイナライザの挙動も考慮に入れて、さらには new() したけれども Dispose() してくれない利用者のことも考慮に入れる必要があります。
また、派生クラスをチームで製作する場合には特に、分業で作業してもバグの少ないコードスタイルがよいでしょう。
下記に示すコードは、安全なリソースの解放を考慮し、継承処理も念頭に置いた IDisposable の実装例です。
もちろん、わざわざプラットフォーム固有の処理をしなくてよいのであれば、単純に Dispose() を継承してかまいません。
なお、以下のコード例では、OnDispose() の処理には、アンマネージなリソースの解放処理と、マネージなリソースの解放処理の両方を判定付きで実装する必要があります。より厳格な運用として、OnDisposeManaged() と OnDisposeUnManaged() を用意する設計も考えられますが、そのあたりはバランスの問題かと思います。
using System; /// <summary> /// <see cref="IDisposable"/> の実装サンプルを提供します。 /// </summary> public class DisposableSample : IDisposable { // 非同期を前提としたクラスの場合でも安全に解放させる場合、 // このクラスの private 以外のメンバに対して // <c>lock(this)</c> による排他が推奨される。 // さらに <c>lock()</c> に成功した後に <see cref="disposed"/> をチェックし // false である場合に処理を進める。 // ただし、上記のような完全スレッドセーフな実装が必要なシーンは多くない。 #region IDisposable /// <summary> /// リソースの解放が行われたかどうかを保持します。 /// </summary> private bool disposed = false; /// <summary> /// リソースの解放が要求された際の処理を行います。 /// </summary> public void Dispose() { // マネージドリソースの解放も依頼する Dispose(true); } /// <summary> /// オブジェクトがガベージ コレクタにより破棄される際の処理を行います。 /// </summary> ~DisposableSample() { // マネージドリソースの解放は完了している Dispose(false); } /// <summary> /// リソースの解放の判定を行います。 /// </summary> /// <param name="isDisposing"><see cref="IDisposable.Dispose"/> メソッドからの呼び出しかどうか。</param> private void Dispose(bool isDisposing) { // 多重解放を避ける lock (this) { if (disposed == false) { // 未処理のため、リソース開放の実処理を実施 OnDispose(isDisposing); disposed = true; if (isDisposing == true) { // ファイナライザで行うべき操作を完了したので、ガベージコレクタに // ファイナライザの呼び出しを抑制してもらう GC.SuppressFinalize(this); } } } } /// <summary> /// リソースの解放の実処理を行います。 /// 本メソッドをオーバーライドする場合は、必ず基底クラスの <see cref="OnDispose(bool)"/> を呼び出してください。 /// </summary> /// <param name="isDisposing"> /// <see cref="Dispose()"/> メソッドからの呼び出しかどうか。 /// true の場合は、マネージドリソースの解放が必要なことを示します。 /// false の場合は、ガベージ コレクタによる呼び出しを示し、マネージドリソースの解放は不要です。 /// </param> /// <example> /// この関数の一般的な実装方法を次の例に示します。 /// <code><![CDATA[ /// protected override void OnDispose(bool isDisposing) /// { /// // 先にマネージドリソースを解放する /// if (isDisposing == true) /// { /// // マネージドリソースの解放 /// if (this.マネージドリソース != null) /// { /// this.マネージドリソース.Dispose(); /// this.マネージドリソース = null; /// } /// } /// /// // アンマネージドリソースの解放 /// if (this.アンマネージドリソース != IntPtr.Zero) /// { /// 解放処理(this.マネージドリソース); /// this.アンマネージドリソース = IntPtr.Zero; /// } /// } /// ]]></code> /// </example> protected virtual void OnDispose(bool isDisposing) { // Do something } #endregion }
コメントする