none
EventHandlerデリゲートをnewしないイベントの登録方法 RRS feed

  • 質問

  • 鈴木@KEGです。
    VS2005 C# で開発しています。

    EventHandler デリゲートの使い方で疑問が出まして、質問させて下さい。

    サンプルコードを書きました。



        public class Foo
        {
            private string message = "";
            public Foo()
            {
                VarEvent varEvent = new VarEvent();
                // IDE で生成されるハンドラ(1)
                varEvent.SampleEvent
                     += new EventHandler(varEvent_SampleEvent);

                // メンバメソッドを指定(2)
                varEvent.SampleEvent
                     += this.SampleEventHandler;

                // メンバメソッドを指定(3)
                //varEvent.SampleEvent += this.SampleEventStaticHandler;    // Error
                varEvent.SampleEvent
                     += new EventHandler(SampleEventStaticHandler);
                message = "1回目";
                varEvent.DemoEvent();
                message = "2回目";
                varEvent.DemoEvent();
                message = "3回目";
                varEvent.DemoEvent();
            }
            // IDE で生成されるハンドラ(1)
            void varEvent_SampleEvent(object sender, EventArgs e)
            {
                Console.WriteLine("varEvent_SampleEvent");
            }
            // (1)を書き換えたメンバメソッド(2)
            void SampleEventHandler(object sender, EventArgs e)
            {
                Console.WriteLine("SampleEventHandler :{0}", message);
            }
            // (3)のstatic メソッド
            static void SampleEventStaticHandler(object sender, EventArgs e)
            {
                Console.WriteLine("SampleEventHandler static");
            }
        }
        //---------------------------------------------------------
        public class VarEvent
        {
            public event EventHandler SampleEvent;
            public void DemoEvent()
            {
                if (SampleEvent != null)
                    SampleEvent(this, EventArgs.Empty);
            }
        }

     

    メンバメソッド で、eventのデリゲートと引数、型が一致していれば、EventHandler デリゲートで new しなくともイベントに登録できるのですが、ならば、EventHandler デリゲートで new すべき理由や用途が分からなくなってしまいました。
    明示的に new と書かなかった場合は、メソッドへの参照はメンバメソッドを持つクラスが保持しているのでしょうか?どの様な仕組みで、こんなコードが成り立つのか、調べても分かりませんでした。

    (3) の例の通り、static メソッドをイベントに登録する場合は、デリゲートのインスタンスを作らなければ、イベントの参照を持つことができないので、エラーとなるのはなんとなく理解できるのですが…

    EventHandler デリゲートにしろ、EventHandler ジェネリックデリゲートでも同様です。

    イベントの登録を外す時に、

    varEvent.SampleEvent -= new EventHandler(varEvent_SampleEvent);

    と、new するのが変な気がしてて、

    varEvent.SampleEvent -= this.SampleEventHandler;

    と、書けるように、EventHandler を new しない様にプログラムしています。
    どの様にするのが、ベストでしょうか?

    2006年12月1日 2:18

回答

  • 青柳です。

    .NET Framework 1.0/1.1 のときは new EventHandler(   ) は必須でした。
    まさに 「eventのデリゲートと引数、型が一致していれば」 これが肝で、「C# コンパイラが型から必要なデリゲートを推測して補ってくれる」 だけです。ですからコードを書くときにちょっと楽になるだけで、生成される IL は同じです (はず)。

     

    2006年12月1日 2:32
  • ILベースで確認しました。(実行はかけていません)

    varEvent.SampleEvent += new EventHandler(varEvent_SampleEvent);

    instance void EventHandlerIL.foo::varEvent_SampleEvent(object,class [mscorlib]System.EventArgs)
    instance void [mscorlib]System.EventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent(class [mscorlib]System.EventHandler)

    varEvent.SampleEvent += varEvent_SampleEvent;

    instance void EventHandlerIL.foo::varEvent_SampleEvent(object,class [mscorlib]System.EventArgs)
    instance void [mscorlib]System.EventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent(class [mscorlib]System.EventHandler)

    まったく変わりません。

    EventHandlerの定義からvoid SampleEventHandler(object sender, EventArgs e) の、
    デリゲートを作る際に自動的に該当するデリゲートを作成します。
    public delegate void EventHandler (Object sender,EventArgs e)


    ちなみに下はSystem.ComponentModel.CancelEventHandlerです。

    varEvent.SampleEvent2 += varEvent_SampleEvent2;

    instance void EventHandlerIL.foo::varEvent_SampleEvent2(object,class [System]System.ComponentModel.CancelEventArgs)
    instance void [System]System.ComponentModel.CancelEventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent2(class [System]System.ComponentModel.CancelEventHandler)


    namespace EventHandlerIL
    {
        class foo
        {
            public foo()
            {
                VarEvent varEvent = new VarEvent();
                varEvent.SampleEvent += new EventHandler(varEvent_SampleEvent);
                varEvent.SampleEvent += varEvent_SampleEvent;
                varEvent.SampleEvent2 += varEvent_SampleEvent2;
            }
            void varEvent_SampleEvent(object sender, EventArgs e)
            {
                Console.WriteLine("varEvent_SampleEvent");
            }
            void varEvent_SampleEvent2(object sender, CancelEventArgs e)
            {
                Console.WriteLine("varEvent_SampleEvent2");
            }
        }
        public class VarEvent
        {
            public event EventHandler SampleEvent;
            public event CancelEventHandler SampleEvent2;
            public void DemoEvent()
            {
                if (SampleEvent != null)
                    SampleEvent(this, EventArgs.Empty);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                foo var = new foo();
            }
        }
    }
    2006年12月1日 3:19

すべての返信

  • 青柳です。

    .NET Framework 1.0/1.1 のときは new EventHandler(   ) は必須でした。
    まさに 「eventのデリゲートと引数、型が一致していれば」 これが肝で、「C# コンパイラが型から必要なデリゲートを推測して補ってくれる」 だけです。ですからコードを書くときにちょっと楽になるだけで、生成される IL は同じです (はず)。

     

    2006年12月1日 2:32
  • 鈴木@KEGです。

    青柳さん 回答ありがとうございます。

    最近、2.0 のコードでnew EventHandler を省略したコードを、海外で良く見かけていたので、コードがすっきりして良いなぁとマネしていましたが、ようやく理由が分かりました。安心して使えます。

    実行時の挙動が、まったく変化しない事は検証したのですが、コンパイラの働きとか気が付きませんでした。
    1.1 から、2.0 への移行は、クラスライブラリの変更以外も押さえとく点が多いですね。

    2006年12月1日 3:12
  • ILベースで確認しました。(実行はかけていません)

    varEvent.SampleEvent += new EventHandler(varEvent_SampleEvent);

    instance void EventHandlerIL.foo::varEvent_SampleEvent(object,class [mscorlib]System.EventArgs)
    instance void [mscorlib]System.EventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent(class [mscorlib]System.EventHandler)

    varEvent.SampleEvent += varEvent_SampleEvent;

    instance void EventHandlerIL.foo::varEvent_SampleEvent(object,class [mscorlib]System.EventArgs)
    instance void [mscorlib]System.EventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent(class [mscorlib]System.EventHandler)

    まったく変わりません。

    EventHandlerの定義からvoid SampleEventHandler(object sender, EventArgs e) の、
    デリゲートを作る際に自動的に該当するデリゲートを作成します。
    public delegate void EventHandler (Object sender,EventArgs e)


    ちなみに下はSystem.ComponentModel.CancelEventHandlerです。

    varEvent.SampleEvent2 += varEvent_SampleEvent2;

    instance void EventHandlerIL.foo::varEvent_SampleEvent2(object,class [System]System.ComponentModel.CancelEventArgs)
    instance void [System]System.ComponentModel.CancelEventHandler::.ctor(object,native int)
    instance void EventHandlerIL.VarEvent::add_SampleEvent2(class [System]System.ComponentModel.CancelEventHandler)


    namespace EventHandlerIL
    {
        class foo
        {
            public foo()
            {
                VarEvent varEvent = new VarEvent();
                varEvent.SampleEvent += new EventHandler(varEvent_SampleEvent);
                varEvent.SampleEvent += varEvent_SampleEvent;
                varEvent.SampleEvent2 += varEvent_SampleEvent2;
            }
            void varEvent_SampleEvent(object sender, EventArgs e)
            {
                Console.WriteLine("varEvent_SampleEvent");
            }
            void varEvent_SampleEvent2(object sender, CancelEventArgs e)
            {
                Console.WriteLine("varEvent_SampleEvent2");
            }
        }
        public class VarEvent
        {
            public event EventHandler SampleEvent;
            public event CancelEventHandler SampleEvent2;
            public void DemoEvent()
            {
                if (SampleEvent != null)
                    SampleEvent(this, EventArgs.Empty);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                foo var = new foo();
            }
        }
    }
    2006年12月1日 3:19
  • ILで確認している間に青柳さんに回答されてしまいましたね。
    まぁ、検証したということで・・・
    2006年12月1日 3:21
  • えムナウさん、検証ありがとうございます。

    まったく変わらないとは。EventHandlerジェネリックでも推測してくれますし、コンパイラも随分と機能追加していますね。
    IL DSAM で表示はしていましたが、ダブルクリックして中身を見ていませんでした…。意味無い。たしかに、えムナウさんの投稿の通り一致しているのが確認できました。

    2006年12月1日 4:27
  •  t_suzuki さんからの引用

    コンパイラも随分と機能追加していますね。

    その反面、先月の投稿にありましたが、初学者がメソッド呼び出しの引数を指定する括弧 () を付け忘れると、delegate のインスタンスとして扱われたコンパイルエラーメッセージを表示してしまうというシチュエーションがふえています。

    ちなみに、勘違いされているようなのですが、static method も new EventHandler(...) は省略できます。static method の場合は this. ではなくて ClassName. です。(this. も ClassName. もスコープが自身で解決可能であれば省略可能です)

     

     

    2006年12月1日 10:01
  •  K.Takaoka さんからの引用

    ちなみに、勘違いされているようなのですが、static method も new EventHandler(...) は省略できます。static method の場合は this. ではなくて ClassName. です。(this. も ClassName. もスコープが自身で解決可能であれば省略可能です)

     


    勘違いしてました…。指摘ありがとうございます。
    落ち着いて考えてみれば、this を付けているからエラーが出ているだけでしたね。

    varEvent.SampleEvent += SampleEventStaticHandler;

    これでコンパイルできました。

    2006年12月4日 8:33