[C#]安全なIDisposable

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
}

トラックバック(0)

トラックバックURL: http://www.hondarer-soft.com/japan/log_cgi/mt-tb.cgi/292

コメントする

ナビゲーション

プロフィール

Photo Hondarer  My status

自分に正直に、目指す物を目指すかたちで、全ての人が幸せになれるシステムを削り出す職人でありたい。

このブログ記事について

このページは、だらりんが2016年6月11日 19:53に書いたブログ記事です。

次のブログ記事は「[C#]継承可能なシングルトンクラス」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

アーカイブ