メソッドをまとめる機能であるデリゲートとデリゲートを用いてイベント処理を行う仕組みについて説明します。GUIアプリを作成する場合などはイベント処理が必須であり、ここでしっかり理解しましょう。
デリゲート
デリゲートとは異なる複数のメソッドを同じ形式で呼び出すための仕組みです。特定の処理を行うときに、メソッドを直接呼び出すのではなく間接的な呼び出しを行うことが可能になり、例えばデリゲートにあらかじめ複数のメソッドを登録しておくことで、そのデリゲートを呼び出すだけで登録されたすべてのメソッドを呼び出すことが可能になります。なお、デリゲート自体は戻り値の型と引数のリストの身を持つ箱で、それに合致するメソッドのみを格納することができます。
デリゲートの実体はMulticastDelegateクラスの派生クラスであり、そのインスタンスは他のメソッドの引数にもなり得ます。
デリゲートの宣言
基本書式
delegate [戻り値データ型] [デリゲート名]([データ型] [引数], ... );
コード例
public delegate int SampleDelegate(int a, int b);
デリゲートはクラスと同じ扱い(デリゲートの実体はMulticastDelegateクラスの派生クラス)なので、デリゲートの宣言はクラス外で行います(サブクラスのようにクラス内で宣言することも可能です)。また、このコード例では先頭にアクセス修飾子の「public」をつけていますが、これもクラスと同じ扱いでこのようにアクセス修飾子をつけることも可能です。
なお、デリゲート自体は引数と戻り値のデータ型を定めただけの単なる「箱」に過ぎないので、よく使われるデリゲートはあらかじめクラスライブラリで宣言されています。例えば以下のような定義済みデリゲートがあり、これらを使うとコード内でデリゲートを宣言する必要はなくなります。
- Actionデリゲート:戻り値のないメソッドを格納するデリゲート
- Func<TResult>デリゲート:戻り値のあるメソッドを格納するデリゲート
- Predicate<T>デリゲート:引数が条件を満たしているかどうかをbool型で返すメソッドを格納するデリゲート
デリゲートのインスタンス化・メソッドの登録
基本書式
[デリゲート] [インスタンス名] = [メソッド];
コード例
//デリゲートに登録するメソッドを用意する
int Addition(int a, int b)
{
return a + b;
}
//デリゲートをインスタンス化し、メソッドを登録する
SampleDelegate d = Addition;
デリゲートの型名に続いてインスタンス名を宣言して、そこにメソッドを代入します。なお、デリゲートのインスタンスには複数のメソッドを登録することも可能であり、デリゲートdに対してメソッドの追加・削除は次のように行います。
//メソッドの追加
d += Method1;
//メソッドの削除
d -= Method1;
なお、デリゲートのインスタンス化はクラスと同様に以下のように書くことも可能です。
SampleDelegate d = new SampleDelegate(Method1);
デリゲートのインスタンス化はクラスのインスタンス化とは異なり、メソッド内でのみ実行可能です。
デリゲートのインスタンスの実行
コード例
Console.WriteLine(d(2, 3));
この例は、先ほど登録したデリゲートインスタンスに引数を入れて実行し、それをコンソール上で表示せたコードです。デリゲートインスタンスには2つの引数の足し算のメソッドが登録されているので、コンソールに表示される結果は「5」となります。なお、デリゲートに複数登録されたメソッドに戻り値があるときは、最後に実行されたメソッドの戻り値のみ返されます。
イベント
イベントとは「マウスのクリックやキーの入力など、プログラムが直接コントロールしていない事象」のことを言います。C#ではこのイベントを処理するために、あらかじめイベントに対して一連のメソッドを登録しておき、1回の呼び出しによってすべて伝える機能を持っています。その実体は、イベントハンドラー(デリゲート)のインスタンスであり、イベント処理に特化した特殊なデリゲートのインスタンスと言えます。
それでは、デリゲートとイベントは何が違うのでしょうか。イベントではデリゲートに対して次のような制約が加わります。
- イベントの呼び出しはそのイベントをメンバーとするクラスの内部のみからしかできない
- 外部からのイベントへのメソッドの登録は、追加(「+=」演算子)と削除(「-=」演算子)のみ可能で、代入(「=」演算子)はできない
デリゲートとイベントの違いについては以下のサイトに詳しく書かれています。
イベントハンドラーとイベントの宣言
イベントハンドラーはデリゲートそのものであり、デリゲートの宣言と全く同様にできます。ここで、クラスライブラリであらかじめ宣言されているイベントハンドラーの引数・戻り値は次のように統一されていることは知っておくと便利です。
イベントの宣言は次のように行います。
基本書式
event [イベントハンドラー(=デリゲート)] [イベント名];
これはデリゲートであるイベントハンドラーのインスタンス化そのものですが、その際にeventキーワードを用いることでイベントとしてインスタンス化することができます。
イベントへのメソッド登録の実際
イベントハンドラーの定義、イベントの生成からイベントに新たなメソッドを登録するまでを見ていきましょう。
まず、イベントハンドラー(=デリゲート)を定義します。
コード例
public delegate void ElapsedEventHandler(object sender, ElapsedEventArgs e);
コード例はElapsedEventHandlerデリゲートの定義です。戻り値のデータ型はvoidで、引数に「object sender」「ElapsedEventArgs e」の2つが定義されています。つまり、このElapsedEventHandlerデリゲートのインスタンスに登録可能なメソッドはObjectクラスとElapsedEventArgsクラスのインスタンスを持っている必要があります。
そのイベントハンドラ―のインスタンスをイベントとして生成します。
コード例
public event ElapsedEventHandler Elapsed;
イベントハンドラー(=デリゲート)のインスタンス化の際にeventキーワードを用いることで、デリゲートのインスタンスではなくイベントとなります。なお、コード例のイベントはElapsedイベントで、Timerクラスにおける指定時間が経過した際の処理を登録するためのイベントです。このようにタイマーやGUI要素などのイベント処理がかかわるものに関しては、クラスライブラリにおいて既にイベントハンドラーのインスタンスとしてイベントが定義されているので、実際には多くの場合そのイベントにメソッドを登録するだけでイベント処理を行うことができます。
それでは、実際にイベントにメソッドを登録する方法を見ていきましょう。まずは、イベントに登録するメソッドを定義します。
コード例
private void OnTimedEvent(Object sender, ElapsedEventArgs e)
{
...
}
最初のElapsedEventHandlerデリゲートの定義において、引数に「object sender」「ElapsedEventArgs e」の2つを指定したので、そのイベントに登録するメソッドもその2つの引数を持っている必要がある点に注意が必要です。では、最後に、Elapsedイベントに今定義したメソッドを追加しましょう。
コード例
Elapsed += OnTimedEvent;
これでElapsedイベントが呼び出されると、OnTimedEventメソッドが呼び出されるようになりました。
コメント