C#のイベント

void Nish(char* szBlog)より
The significance of the C# event keyword
ここを読みながら、私がC#を勉強したときにピンとこなかったのがイベントだったなぁと振り返ってみたり。

単純なイベントの例。

using System;

class MyEventArgs {
  private object args;
  public MyEventArgs(object o) { args = o; }
  public object Args { get { return args; } }
}

delegate void MyEventHandler(MyEventArgs e);

class TheClass {
  public event MyEventHandler MyPublicEvent;

  public void OnPublicEvent(MyEventArgs e) {
    if (MyPublicEvent != null)
      MyPublicEvent(e);
  }

  public static void Main() {
    TheClass the = new TheClass();
    the.MyPublicEvent +=new MyEventHandler(the_MyPublicEvent);
    the.OnPublicEvent(new MyEventArgs("from TheClass.Main"));
  }

  private static void the_MyPublicEvent(MyEventArgs e) {
    Console.WriteLine("MyPublicEvent called. (args={0})", e.Args);
  }
}

分かり難い点の1つにイベントが設定されていないときはnullになるので、nullチェックしてイベント呼び出ししますが、この動きが見えないこと。イベントは裏方で色々やってくれるので便利な反面、見通しが悪いです。次にアクセス修飾子の意味合いが見た目上では違うことです。

using System;

class MyEventArgs {
  private object args;
  public MyEventArgs(object o) { args = o; }
  public object Args { get { return args; } }
}

delegate void MyEventHandler(MyEventArgs e);

class BaseClass {
  public event MyEventHandler MyPublicEvent;
  protected event MyEventHandler MyProtectedEvent;
  private event MyEventHandler MyPrivateEvent;

  public BaseClass() {
    MyPrivateEvent += new MyEventHandler(this.MyService);
  }

  public virtual void OnPublicEvent(MyEventArgs e) {
    if (MyPublicEvent != null) 
      MyPublicEvent(e);
  }

  protected virtual void OnProtectedEvent(MyEventArgs e) {
    if (MyProtectedEvent != null) 
      MyProtectedEvent(e);
  }

  private void MyService(MyEventArgs e) {}
}

class DerivedClass : BaseClass {
  public void OnAnotherPublicEvent(MyEventArgs e) {
    /* 派生クラスであっても、イベントは呼び出せない
    if (MyPublicEvent != null)
      MyPublicEvent(e);
    */
    base.OnPublicEvent(e);
  }

  protected override void OnProtectedEvent(MyEventArgs e) {
    Console.WriteLine("Before");
    // OnProtectedEvent経由でMyProtectedEventを呼ぶ
    base.OnProtectedEvent(e);
    Console.WriteLine("After");
  }
}

ベースクラスにpublic/protected/privateのイベントを用意しています。例えばMyProtectedEventは派生クラスから呼び出せるように見えますが、実際はイベントを定義したクラス以外からは呼び出せません。イベントのアクセス修飾子は通知先であるdelegateの追加/削除に関してなんですよね。覚えてしまえば大した話ではないのですが、ちょっと取っつきにくいです。