C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2007/01/14 14:23
以前 Dispose Finalizeパターン という記事で解説したDispose Finalizeパターンを実装するクラスの、テンプレートを作ってみました。

【 Download 】
以下のリンクをクリックするとDownloadページへ移動するので、移動先からダウンロードしてください。

DisposableClass.zip


【 インストール方法 】
Visual Studio ユーザー項目テンプレートフォルダ ( 通常は "マイ ドキュメント\Visual Studio 2005\Templates\ItemTemplates" ) の "Visual C#" フォルダに、ダウンロードしたファイル ( DisposableClass.zip ) を配置するだけです。

Visual Studio ユーザー項目テンプレートフォルダは、Visual Studio 2005 の [ ツール ] - [ オプション ] - [ プロジェクトおよびソリューション ] - [ 全般 ] - [ Visual Studio ユーザー項目テンプレートの場所 ] で設定されている場所です。


【 使用方法 】
DisposableClass.zipをインストールすると、C#のプロジェクトに「新しい項目の追加」を行う際に "Disposable クラス" というテンプレートが選択できるようになります。

新しい項目の追加は、以下の方法で行えます。
  • [ プロジェクト ] - [ 新しい項目の追加 ] を選択する
  • プロジェクトのコンテキストメニューで [ 追加 ] - [ 新しい項目の追加 ] を選択する
  • Ctrl + Shift + A を押す
このテンプレートを使用すれば、Dispose(bool)メソッド内にリソースの解放処理を追加するだけで、Dispose Finalizeパターンを実装したことになります。


【 使用例 】
ConsoleApplication1プロジェクト ( デフォルトの名前空間もConsoleApplication1 ) で、このテンプレートを使ってDisposableClass1.csを作成した場合、以下のようなコードファイルが作成されます。

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class DisposableClass1 : IDisposable
    {
        public DisposableClass1()
        {
            this.InitializeDisposeFinalizePattern();
        }

        public void Method1()
        {
            this.ThrowExceptionIfDisposed();
            // Method1 の処理をこの位置に記述します。
        }

        #region Dispose Finalize パターン

        /// <summary>
        /// 既にDisposeメソッドが呼び出されているかどうかを表します。
        /// </summary>
        private bool disposed;

        /// <summary>
        /// ConsoleApplication1.DisposableClass1 によって使用されているすべてのリソースを解放します。
        /// </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            this.Dispose(true);
        }

        /// <summary>
        /// ConsoleApplication1.DisposableClass1 クラスのインスタンスがGCに回収される時に呼び出されます。
        /// </summary>
        ~DisposableClass1()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// ConsoleApplication1.DisposableClass1 によって使用されているアンマネージ リソースを解放し、オプションでマネージ リソースも解放します。
        /// </summary>
        /// <param name="disposing">マネージ リソースとアンマネージ リソースの両方を解放する場合は true。アンマネージ リソースだけを解放する場合は false。 </param>
        protected virtual void Dispose(bool disposing)
        {
            if (this.disposed)
            {
                return;
            }
            this.disposed = true;

            if (disposing)
            {
                // マネージ リソースの解放処理をこの位置に記述します。
            }
            // アンマネージ リソースの解放処理をこの位置に記述します。
        }

        /// <summary>
        /// 既にDisposeメソッドが呼び出されている場合、例外をスローします。
        /// </summary>
        /// <exception cref="System.ObjectDisposedException">既にDisposeメソッドが呼び出されています。</exception>
        protected void ThrowExceptionIfDisposed()
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }
        }

        /// <summary>
        /// Dispose Finalize パターンに必要な初期化処理を行います。
        /// </summary>
        private void InitializeDisposeFinalizePattern()
        {
            this.disposed = false;
        }

        #endregion
    }
}



【 参考 】
独自の項目テンプレートを作成するには?[VS 2005のみ] - @IT
2006/07/23 16:37
クラスがアンマネージリソースをフィールドに保持する場合、またはIDisposableインターフェイスを実装するクラスをフィールドに保持する場合、そのクラスはIDisposableインターフェイスを実装する必要がある。
IDisposableを実装する際は以下の要件を満たすために、Dispose Finalizeパターンを適用する。


  • Dispose()はマネージリソースとアンマネージリソースの解放処理を行う。

  • デストラクタはアンマネージリソースの解放処理を行う。

  • Dispose()が呼び出された場合、デストラクタがGCによって呼び出されることのないようにする。

  • Dispose()は複数回呼び出されても良いようにする。

  • Dispose()、デストラクタでは、例外がスローされないようにする。

  • Dispose()が呼び出された後にDispose()以外のパブリックメソッドを呼び出すとObjectDisposedExceptionがスローされるようにする。


Dispose Finalize パターンpublic class Class1 : IDisposable
{
    private bool disposed;

    public Class1()
    {
        this.disposed = false;
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
        this.Dispose(true);
    }

    ~Class1()
    {
        this.Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (this.disposed)
        {
            return;
        }
        this.disposed = true;
        if (disposing)
        {
            // マネージリソースの解放処理
        }
        // アンマネージリソースの解放処理
    }

    public void Method1()
    {
        this.ThrowExceptionIfDisposed();
        // Method1の処理
    }

    protected void ThrowExceptionIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().ToString());
        }
    }
}

void Dispose(bool disposing)メソッドは、クラスがシールドクラスでない場合は上記のようにプロテクトな仮想メソッドとし、クラスがシールドクラスである場合はプライベートなメソッドとする。

// 追記
ThrowExceptionIfDisposed()メソッドはDispose Finalizeパターンに含まれていないが、このメソッドをプロテクトメソッドとして用意することにより、派生クラスで定義したメソッドでも Dispose済みかどうかを簡単に調べる(というかObjectDisposedExceptionをスローする)ことができる。

// 追追記
Dispose Finalizeパターンを実装するクラスのテンプレートを作成しました。このテンプレートを使えば、Dispose Finalizeパターンを簡単に実装できます。

Dispose Finalizeパターンを実装するクラスのテンプレート