トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

Dispose, Finalize の調査

[カテゴリ:言語]
[カテゴリ:.NET]
[カテゴリ:C#]

Dispose, Finalize のタイミング調査

親でDispose,Finalizeを実装しているが、子は実装しなかったとき。

条件

  • 親(MyClassA)
    • Dispose実装
    • Finalize実装
  • 子(MyClassB)
    • MyClassAを継承
    • Dispose未実装
    • Finalize未実装

結論

このときのMyClassBの挙動は。

  • usingあり → MyClassAのDisposeが呼ばれる。
  • usingなし → MyClassAのFinalizeが呼ばれる。

つまり、親でDispose,Finalizeを実装していれば、子で実装せずとも親のものが呼ばれる。

ソース

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string txt = "";
            txt += "clr version (runtime)  : " + System.Environment.Version.ToString() + "\r\n";
            txt += "os version             : " + System.Environment.OSVersion.ToString() + "\r\n";
            txt += "  platform : " + System.Environment.OSVersion.Platform.ToString() + "\r\n";
            txt += "  version  : " + System.Environment.OSVersion.Version.ToString() + "\r\n";
            txt += "clr version (buildtime): " + System.Reflection.Assembly.GetExecutingAssembly().ImageRuntimeVersion + "\r\n";
            Console.WriteLine(txt);

            Console.WriteLine("---- 開始");

            Console.WriteLine("---- A生成 usingあり");
            using (MyClassA tmp_a1 = new MyClassA("A生成 usingあり")) { };

            Console.WriteLine("---- A生成 usingなし");
            MyClassA tmp_a2 = new MyClassA("A生成 usingなし");

            Console.WriteLine("---- B生成 usingあり");
            using (MyClassB tmp_b1 = new MyClassB("B生成 usingあり")) { };

            Console.WriteLine("---- B生成 usingなし");
            MyClassB tmp_b2 = new MyClassB("B生成 usingなし");

            Console.WriteLine("---- 終了");
        }
    }

    //
    // 親クラス
    //   Dispose、Finalize(=C#ではデストラクタと等価) を実装
    //
    class MyClassA : IDisposable
    {
        protected String _name = "";

        public MyClassA(String name)
        {
            this._name = name;
            string func_ = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name + ":" + System.Reflection.MethodInfo.GetCurrentMethod().Name + "() ";
            Console.WriteLine("{0} > {1}", this._name, func_);
        }

        ~MyClassA()
        {
            string func_ = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name + ":" + System.Reflection.MethodInfo.GetCurrentMethod().Name + "() ";
            Console.WriteLine("{0} > {1} Disposeが実行されていません。", this._name, func_);
        }

        void IDisposable.Dispose()
        {
            string func_ = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name + ":" + System.Reflection.MethodInfo.GetCurrentMethod().Name + "() ";
            Console.WriteLine("{0} > {1}", this._name, func_);
            System.GC.SuppressFinalize(this);
        }
    }

    //
    // 子クラス
    //   Disposeは、未実装(親がDisposeを実装しているので本来は実装すべき)
    //   Finalizeは、未実装(親クラスのものが呼ばれる)
    //
    class MyClassB : MyClassA
    {
        public MyClassB(String name) : base(name)
        {
            string func_ = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name + ":" + System.Reflection.MethodInfo.GetCurrentMethod().Name + "() ";
            Console.WriteLine("{0} > {1}", this._name, func_);
        }

// このコメントを生かすと子→親の順でFinalizeが実行される。
//      ~MyClassB()
//      {
//          string func_ = System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name + ":" + System.Reflection.MethodInfo.GetCurrentMethod().Name + "() ";
//          Console.WriteLine("{0} > {1} Disposeが実行されていません。", this._name, func_);
//      }
    }
}

実行結果

clr version (runtime)  : 2.0.50727.3053
os version             : Microsoft Windows NT 5.1.2600 Service Pack 2
  platform : Win32NT
  version  : 5.1.2600.131072
clr version (buildtime): v2.0.50727

---- 開始
---- A生成 usingあり
A生成 usingあり > MyClassA:.ctor()
A生成 usingあり > MyClassA:System.IDisposable.Dispose()
---- A生成 usingなし
A生成 usingなし > MyClassA:.ctor()
---- B生成 usingあり
B生成 usingあり > MyClassA:.ctor()
B生成 usingあり > MyClassB:.ctor()
B生成 usingあり > MyClassA:System.IDisposable.Dispose()
---- B生成 usingなし
B生成 usingなし > MyClassA:.ctor()
B生成 usingなし > MyClassB:.ctor()
---- 終了
B生成 usingなし > MyClassA:Finalize()  Disposeが実行されていません。
A生成 usingなし > MyClassA:Finalize()  Disposeが実行されていません。

Disposeの呼び出し呼び出し忘れを検出する方法を考える。

.NET の問題: ファイナライザをデバッグする
http://msdn.microsoft.com/ja-jp/magazine/cc163324.aspx

CLR 徹底解剖: IDisposable について
http://msdn.microsoft.com/ja-jp/magazine/cc163392.aspx

リークが発生するアプリケーションのデバッグ: マネージ コードでのメモリ リークの識別と回避
http://msdn.microsoft.com/ja-jp/magazine/cc163491.aspx

WinDbg+SOS.dll の組み合わせで finalizeの数を見ることが出来る。
CLR 徹底解剖: メモリの問題を調べる
http://msdn.microsoft.com/ja-jp/magazine/cc163528.aspx

最終更新時間:2009年02月12日 02時51分44秒