イベントを理解する - C#

「リフレクションでイベントハンドラを取得したい。」
では「イベントハンドラを取得するために必要なことは何か?」を考え、そのためにはイベントの仕組みを理解する必要があるので今一度イベントの仕組みをここで確認したい。

以下に掲載されたコードを例に話を進めます。


オブジェクト(コントロール等)がイベントに登録されたイベントハンドラをどのようにして保持しているか?

既存のオブジェクト(コントロール等)であればSystem.ComponentModel名前空間のComponentクラスから派生し、このComponentクラスがイベントハンドラを格納するEventHandlerList型の"Events"というプロパティを持っている。

[F12]キーを押すと定義を見ることが出来ます。こんな感じで
protected EventHandlerList Events { get; }
既存のコントロールのイベントハンドラは通常この"Events"に格納される。全てがこれに格納されるわけではない。コントロールによっては用途によって複数に分けて格納している。(例えばフォームなど)
protectedなのが気になるが…


EventHandlerListはどのように値を保持するのか?

以下の通り。
public Delegate this[object key] { get; set; }
つまり、EventHandlerListはDelegate型のインデクサでobject型のインデックスを使用する。
連想配列に似た方法でキーを文字列や数値ではなくオブジェクトとしているところがポイントでしょうか。
イベントハンドラの追加/削除には、通常それ用に公開された以下のメソッドを使用して行う。
public void AddHandler(object key, Delegate value);
public void RemoveHandler(object key, Delegate value);


マルチキャスト デリゲートは += でメソッドを幾つもブチ込んで実行できる。
つまり、メソッドのポインタを格納するListをイメージすればイイ。
Events[key]によって取得するデリゲート型のイベントハンドラはたとえ複数であったとしても配列ではない


Eventsに格納されたイベントハンドラを取得するには以下のように行う。
Delegate handler = Events[key];


個々のデリゲートオブジェクトを取得するには、GetInvocationListを使用する。
Delegate[] h = handler.GetInvocationList();
イベントハンドラをDelegate型の配列として取得している。


取得したイベントハンドラを実行するには、DynamicInvokeを使用する。
handler.DynamicInvoke(new object[] {this, e });


実際にはこのように使用することになる。
protected void RaiseEvent(object key, EventArgs e)
{
    Delegate handler = Events[key];
    if (handler != null)
    {
        handler.DynamicInvoke(new object[] { this, e });
    }
}


まとめ

イベントハンドラを取得するには
  • イベントハンドラを格納する変数(上記例ではEvents)
  • そのイベントハンドラを取りだすkeyとなるオブジェクト(mouseDownEventKeyなど)
この二つを取得する必要がある、ということになる。

私の知る限りでは、既存のオブジェクトで使用されているkeyの名前には全てではありませんが『"Event" + イベント名』という規則性が当てはまることが多い。

しかし、どちらも既存のオブジェクトを対象とするなら、変数名は一定の規則に従って名前付けが行われているだろうが、サードパーティ製のモノとなればこれらを取得するのは困難かもしれない。

0 Comments:

Recent Posts