Seasons.NET

ちょっとした技術ブログです

delegateを知る

delegate使って、ちょっとおもしろいことを。
delegate内でループ変数jの値を使って、各フォームが閉じられた時に
targets[j].Flg = trueにしようとしてますが、
ループを抜けた後、このjは、j = 3をさすことになるので、
アクセス違反になってまいます。

そこで、一度targetsの参照クラスを用意して、そのクラスをdelegate内で
渡せば、うまくいくことになります。
この時ローカル変数を渡していることになり、C++,Cで組んできた人は、
混乱するかもしれませんが、ヘルパクラスをコンパイラが用意してくれるので
このコードは、エラーになりません。

  1 namespace DisposeSample
  2 {
  3     using System;
  4     using System.Collections;
  5     using System.Windows.Forms;
  6     using MbUnit.Framework;
  7 
  8     /// <summary>
  9     /// HogeHogeクラス
 10     /// </summary>
 11     class HogeHoge
 12     {
 13         private bool flg = false;
 14 
 15         public HogeHoge(bool initFlg)
 16         {
 17             flg = initFlg;
 18         }
 19 
 20         public bool Flg
 21         {
 22             get { return flg; }
 23             set { flg = value; }
 24         }
 25     }
 26 
 27     class Program
 28     {
 29         private const int objNum = 3;
 30         static void Main()
 31         {
 32         }
 33 
 34         [TestFixture]
 35         public class DelegateTest
 36         {
 37            [Test]
 38            public void TestFunc()
 39            {
 40                HogeHoge targets = new HogeHoge[objNum];
 41                Form frms = new Form[targets.Length];
 42                for(int i = 0;i < targets.Length;i++)
 43                    targets[i] = new HogeHoge(false);
 44 
 45                // --------------------------------------------------------- //
 46                // テストその1
 47                // --------------------------------------------------------- //
 48                for(int j = 0;j < targets.Length;j++)
 49                {
 50                    frms[j] = new Form();
 51                    frms[j].Closed += delegate
 52                    {
 53                        // アクセスしようとしているインデックスを表示
 54                        MessageBox.Show("Index is " + j.ToString());
 55                        if( j < targets.Length )
 56                             targets[j].Flg = true;
 57                    };
 58                    frms[j].KeyDown += delegate(object sender,KeyEventArgs e)
 59                                           {
 60                                                (sender as Form).Close();
 61                                           };
 62                    frms[j].ShowDialog();
 63                }
 64                TargetTest(targets);
 65 
 66                // --------------------------------------------------------- //
 67                // テストその2
 68                // 
 69                // これは成功しない
 70                // 
 71                // jがまだ生きていて、このjは、ループ終了後の3を指している。
 72                // 当然、j = 3は、確保されていないのでアクセス不可能
 73                // テストその3を実行する時は、コメントにしてください
 74                // --------------------------------------------------------- //
 75            #if TestDelegate
 76 
 77                TargetInit(targets);
 78                ShowForms(frms);
 79                TargetTest(targets);
 80 
 81            #endif
 82 
 83                // --------------------------------------------------------- //
 84                // テストその3
 85                // --------------------------------------------------------- //
 86                // delegateに渡す際に、インデックスを渡すと上記の不具合が
 87                // 発生するので、以下のように参照オブジェクトを用意してあげるとよい
 88                for(int k = 0;k < targets.Length;k++)
 89                {
 90                    // 一時的に参照オブジェクトに入れてdelegateに渡す
 91                    // もしくは、基底クラス(Form)から派生して、
 92                    // 参照オブジェクトのメンバ(HogeHoge)をもたせるとか
 93                    HogeHoge temp = targets[k];
 94                    frms[k].Closed += delegate { temp.Flg = true; };
 95                }
 96                TargetInit(targets);
 97                ShowForms(frms);
 98                TargetTest(targets);
 99 
100            }
101 
102             /// <summary>
103             /// ターゲットテスト
104             /// </summary>
105             /// <param name="targets"></param>
106             private void TargetTest(HogeHoge targets)
107             {
108                 foreach(HogeHoge hoge in targets)
109                     Assert.AreEqual(true,hoge.Flg);
110             }
111 
112             /// <summary>
113             /// ターゲット初期化
114             /// </summary>
115             /// <param name="targets"></param>
116             private void TargetInit(HogeHoge targets)
117             {
118                foreach(HogeHoge target in targets)
119                    target.Flg = false;
120             }
121 
122             /// <summary>
123             /// フォーム表示
124             /// </summary>
125             /// <param name="frms"></param>
126             private void ShowForms(Form[] frms)
127             {
128                // もう一度表示
129                foreach(Form frm in frms)
130                    frm.ShowDialog();
131             }
132         }
133     }
134 }