C# 入門

ホーム > イベント > なぜ event キーワードを指定するのか?

なぜ event キーワードを指定するのか?

イベントはデリゲートで実装されます。

しかし後述のようにイベント処理は .NET Framework では event キーワードをつけて実装します。

では event はどんな問題を解決するのでしょうか。

プレーンな (素の) デリゲートを用いてイベント処理を実装した場合の問題点について説明します。

ちなみに、デリゲート については理解している前提で書いてますので、 不安な方は読み直して置いてください。

event キーワードなしのデリゲートによるイベント処理

ここでは例として、ボタンを表すようなクラスを実装してみましょう。単純なクリックイベントを次のように定義します。

namespace EventTest1
{
  class MyButton
  {
    public delegate void ClickHandler(object sender, string msg);
    public ClickHandler Click;

    public void DummyClick()
    {
      if (Click != null)
      {
        Click(this, "Clicked!");
      }
    }
  }
}

ここではデリゲートを "ナントカ Delegate" としないで、イベントハンドラの一般的な書き方にならって、"ナントカ Handler" としています。 が、要はデリゲートです。

DummyClick メソッドはこの "ボタン" をクリックしたことをエミュレートするためのダミーメソッドです。DummyClick メソッドを呼び出した、 ということはこのボタンのクリックイベントを発生させるきっかけになるものだ、ということにします。

さて、これを利用する側のコードは次のようになります。

using System;

namespace EventTest1
{
  class Program
  {
    static void Main(string[] args)
    {
      MyButton button = new MyButton();
      button.Click = Button_Click1;
      button.DummyClick();
    }

    static void Button_Click1(object sender, string msg)
    {
      Console.WriteLine("1:" + msg);
    }
  }
}

ボタンのインスタンスを作り、イベントハンドラとして Button_Click1 メソッドを設定。その後、DummyClick メソッドを呼び出しています。

実行結果は次の通りです。

1:Clicked!

ここまでは全く問題なく "イベント処理" されているように見えますね。では何が問題なのでしょうか。

インボケーションリストが暗黙的に取り消される問題

デリゲートはいわゆるマルチキャストデリゲートをサポートします。 デリゲートはインボケーションリストを持っていて、複数のメソッドを呼び出すことが可能です。

しかし、ハンドラ Button_Click2 を定義して、それもインボケーションリストに追加したいとします。そのためのコードは次の通りです。

using System;

namespace EventTest1
{
  class Program
  {
    static void Main(string[] args)
    {
      MyButton button = new MyButton();
      button.Click = Button_Click1;
      button.Click += Button_Click2;
      button.DummyClick();
    }

    static void Button_Click1(object sender, string msg)
    {
      Console.WriteLine("1:" + msg);
    }

    static void Button_Click2(object sender, string msg)
    {
      Console.WriteLine("2:" + msg);
    }
  }
}

実行結果は次の通り。

1:Clicked!
2:Clicked!

確かに二つのハンドラが呼び出されています。

しかしここで間違えて、次のように書いたとします。

using System;

namespace EventTest1
{
  class Program
  {
    static void Main(string[] args)
    {
      MyButton button = new MyButton();
      button.Click = Button_Click1;
      button.Click = Button_Click2; // BUG! += 
      button.DummyClick();
    }

    static void Button_Click1(object sender, string msg)
    {
      Console.WriteLine("1:" + msg);
    }

    static void Button_Click2(object sender, string msg)
    {
      Console.WriteLine("2:" + msg);
    }
  }
}

ここでは += ではなく、= としてハンドラを追加しています。この実行結果は次の通り。

2:Clicked!

ハンドラ Button_Click1 が呼び出されていません。これは = としたことによって、デリゲートのインボケーションリストがクリアーされて、 Button_Click2 のみとなったためです。

= と += の間違いは、原因を突き止め難いバグとなりがちなので、ハンドラを外すのであれば明示的に、-= オペレータで外したいところです。

このように、 = を認めることによって引き起こされる問題点が一点目です。

直接デリゲートを呼び出す問題

もうひとつの問題はイベントを直接呼び出せる問題です。

上記では MyButton クラスの Click デリゲートは public メンバーなので、実は次のように直接呼び出すことも可能です。

using System;

namespace EventTest1
{
  class Program
  {
    static void Main(string[] args)
    {
      MyButton button = new MyButton();
      button.Click = Button_Click1;
button.Click(null, "Hello!");
} static void Button_Click1(object sender, string msg) { Console.WriteLine("1:" + msg); } } }

この実行結果は次の通り。"正常動作" してしまいます。

1:Hello!

しかし本来クリックイベントは、本当のクリックが発生したときにのみ発生して欲しいものです。 このように外部からイベントハンドラを呼び出せる問題が2点目です。

主にこれら二点の問題を解決するために、event キーワードを付けてイベントを定義します。

ホーム > イベント > なぜ event キーワードを指定するのか?