デリゲートとはC++などに存在する関数ポインタに近い機能を持つもので、クラスやメソッドからのコールバック、GUIのイベントハンドラなどを実現するための機構として存在しています。 デリゲートを用いることで任意のメソッドを変数のように扱うことができるようになり、ラムダ式や匿名メソッドなども記述することができるようになっています。
ここではデリゲートの基本的なことについての解説と、デリゲートとメソッドの関わり、デリゲートの宣言など関連する構文などについて解説します。
デリゲートとは
はじめにデリゲートとは何か、またデリゲートの使い方・動作について理解するため、簡単な例を使って見ていきます。 次の例は、デリゲートを使ってメソッド呼び出しを行う例です。
このように、デリゲートを使うことで任意のメソッドを変数に格納して呼び出すことが出来るようになります。 デリゲートを使ったメソッドの呼び出しは、通常のメソッド呼び出しと見た目の変わりはありません。 デリゲートを使ったメソッド呼び出しの結果は、当然デリゲートが参照するメソッドの動作次第となります。
もう1つ別の例として、デリゲートを使ってコールバックを実現する例を見ていきます。 まずは次のような一定時間待機するだけの単純なコードを用意します。
このコードのWaitメソッドを書き換え、待機が完了したらコールバックを行うようにしてみます。 コールバックするメソッドはデリゲートであるAction型の引数callbackActionで受けとるようにします。
実行結果のとおり、Waitメソッドにおいてデリゲートを通してPrintTimeメソッドが呼び出されていることが分かると思います。
このように、デリゲートにはある特定のメソッドを参照する役割を持っています。 デリゲートを使用する場合、メソッドを呼び出す側は直接特定のメソッドを呼び出す必要はなく、デリゲートが表すメソッドを間接的に呼び出すことが出来ます。
言い換えると、デリゲートを用いることで任意のメソッドを一種の変数や引数のように扱うことが出来ます。 これにより、デリゲートを指定する側からすれば呼び出させるメソッドを自由に指定することができ、デリゲートを用いてメソッドを呼び出す側からすれば呼び出しは行うが実際にどのようなメソッドが呼び出されるかは感知しないということになります。 delegateという動詞には「委譲する、委任する」、名詞では「代表、代理人」といった訳語が用いられることを知っておくとデリゲートの概念を大雑把に把握することができると思います。
上記の点をより明確にするために、先の例を書き換え他のコールバックメソッドも呼び出すように変えてみます。 Waitメソッドには変更を加えていない点に注目してください。
ここまでの例では、引数なし・戻り値なし(void)のデリゲートActionを使用していますが、デリゲートの型とメソッドの引数・戻り値が一致していれば引数や戻り値のあるメソッドもデリゲートに指定することが出来ます。 次の例では、T型の引数を一つとるジェネリックデリゲートAction<T>を使ってコールバックを行っています。
なお、ここまでの例で使用してきたActionデリゲートは.NET Frameworkに用意されている汎用的なデリゲートですが、デリゲート型を独自に宣言することもできます。
さらに、ここまでの例では単一のメソッドを参照するデリゲートを使用してきました(シングルキャスト)。 いくつかのデリゲートを連結して一つのデリゲートを作成し、複数のメソッドを呼び出すようにすることも出来ます(マルチキャスト)。
マルチキャストについてはデリゲートの機能 §.マルチキャストとデリゲートの連結・削除 (Combine, Remove)で詳しく解説します。
デリゲートとメソッド
メソッドからデリゲートをするには、メソッドとデリゲートのシグネチャ(signature、引数と戻り値の型の組み合わせ)が一致している必要があります。 関数ポインタとは異なり、デリゲートはタイプセーフであり、シグネチャが一致していないメソッドからデリゲートを作成することは出来ません。
先の例で使用したActionデリゲートは引数なし・戻り値なし(void)のメソッドを表すデリゲートです。 これと異なるシグネチャのメソッドからはActionデリゲートを作成することは出来ません。
また、静的(共有)メソッドだけでなく、インスタンスメソッドを設定することも出来ます。 デリゲートは、メソッドを呼び出す際にインスタンスを区別して呼び出します。
さらに、オーバーライドされたメソッドや、(デリゲートが作成出来る場合は)非パブリックなメソッドでも呼び出すことが出来ます。
なお、デリゲートのMethodプロパティやTargetプロパティを参照することにより、デリゲートが実際に呼び出すメソッドやインスタンスを知ることが出来ます。
デリゲートと構文
メソッドからデリゲートのインスタンスを作成するには、ここまでの例で挙げてきたようにデリゲートのコンストラクタを使う他にも、言語によって用意されている構文を使うことが出来ます。 C#ではキャストや単純な代入、VBではAddressOf演算子を使うことでコンストラクタを呼び出さなくてもデリゲートを作成することが出来ます。 無論、デリゲートの型とメソッドのシグネチャが一致していなければコンパイルエラーとなります。
匿名メソッドを使ったデリゲートの作成
匿名メソッド(C# 2.0以降)の構文を使う事によってもデリゲートを作成することができます。
上記コード中においてコメントアウトしてあるコードは、匿名メソッドとの比較のために通常のメソッドに似せた擬似コードを記述したものです。 実際にはコンパイルできません。
ラムダ式を使ったデリゲートの作成
ラムダ式(C# 3.0、VB10)の構文を使う事によってもデリゲートを作成することができます。
上記コード中においてコメントアウトしてあるコードは、ラムダ式との比較のために通常のメソッドに似せた擬似コードを記述したものです。 実際にはコンパイルできません。
上記のコードはいずれも等価なもので、以下のような実行結果となります。
デリゲートの宣言
.NET Frameworkでは多くのデリゲート型が用意されていますが、独自に宣言して使用することも出来ます。 C#ではdelegateキーワード、VBではDelegateステートメントを使って宣言します。 いずれも実体を持たないメソッドの宣言のような記述になっています。
なお、デリゲート型に対するアクセス修飾子はあくまでデリゲート型に対して適用されるもので、デリゲートが表すメソッドのアクセス修飾子とは無関係です。
汎用的なデリゲート
多くの場合、独自にデリゲート型を宣言するよりも、.NET Frameworkで用意されているデリゲート型を使うことになります。 以下は、よく使われるデリゲート・汎用的なデリゲートの一例です。
名前空間 | デリゲート型 | 主な用途 |
---|---|---|
System | AsyncCallback | 非同期呼び出しが完了した場合に呼び出されるコールバックメソッドを指定するために用いられる。 |
Comparison<T> | コレクション型のクラスで二つのT型の値の大小関係を比較するメソッドを指定するために用いられる。 例: 大小関係の定義と比較 §.Comparison<T>、基本型のソートと昇順・降順でのソート §.Sort + Comparison<T>、ジェネリックなソートアルゴリズムの実装 |
|
Converter<TInput, TOutput> | コレクション型のクラスでTInput型の値をTOutput型に変換するメソッドを指定するために用いられる。 例: 配列操作 §.全要素の変換 (ConvertAll)、ジェネリックコレクション(1) List §.型変換・全要素の変換 (ConvertAll)、ユーザ定義の型変換 §.Converter<TInput, TOutput> |
|
Predicate<T> | コレクション型のクラスでT型の値に対して真偽値を返す述語となるメソッドを指定するために用いられる。 例: 配列操作 §.述語を使った検索 (Find, Exists, etc.)、ジェネリックコレクション(1) List §.述語(Predicate)を用いた検索 |
|
EventHandler
EventHandler<TEventArgs> |
各種イベントのハンドラとなるメソッドを指定するために用いられる。 | |
Action
Action<T> Action<T1, T2> Action<T1, T2, T3>など |
戻り値のないメソッドを表すデリゲート。 渡された引数に応じて任意の処理を行うメソッドを指定するために用いられる。 | |
Func<TResult>
Func<T, TResult> Func<T1, T2, TResult> Func<T1, T2, T3, TResult>など |
TResult型の戻り値を返すメソッドを表すデリゲート。 渡された引数に応じて任意の処理を行った結果を返すメソッドを指定するために用いられる。 | |
System.Threading |
ThreadStart
ParameterizedThreadStart |
Threadクラスでスレッドを作成する場合に、スレッドとして起動するメソッドを指定するために用いられる。 |
WaitCallback | ThreadPoolクラスでスレッドプールを使って別スレッドで動作させるメソッドを指定するために用いられる。 | |
System.Net.Security | RemoteCertificateValidationCallback | SslStreamクラスでリモートサーバから受信したSSL証明書の妥当性を検証するメソッドを指定するために用いられる。 |
System.Text.RegularExpressions | MatchEvaluator |
Regexクラスで正規表現にマッチする箇所が見つかる度にコールバックされ、別の文字列に置き換えるメソッドを指定するために用いられる。 例: 正規表現によるパターンマッチングと文字列操作 §.マッチ箇所の置換 (Regex.Replace) |
System.Xml.Schema | ValidationEventHandler | XmlReaderクラスやXmlValidatingReaderクラスでXMLスキーマによるXML文書の検証でエラーや警告が発生した場合にコールバックされるメソッドを指定するために用いられる。 |
以下はList<T>クラスといくつかのデリゲートを使った例です。 この例で使用しているメソッドについてはList<T>についての解説を参照してください。