デリゲート (プログラミング)デリゲート (英: delegate) とは、主にC#、Visual Basic .NETなどの、.NET環境向けのプログラミング言語(.NET言語)に用意されている機能であり、参照型の一種(デリゲート型)である。 概要デリゲートは、オブジェクトインスタンスへの参照とメソッドへの参照をペアにしてカプセル化するものである。概念としてはC言語やC++の関数ポインタに近いが、デリゲートは完全なオブジェクト指向である[1]。型安全であるという特徴がある[2]。2002年にリリースされた.NET Frameworkでは Object Pascal(Delphi)の「インスタンスのメソッドへのポインタを格納する、メソッドポインタ」と同様のものである。また、Microsoft Visual J++も、Javaと非互換のデリゲートを導入したが、.NET Frameworkのデリゲートはこれらを発展させたものである[注釈 1]。 なお、C++における「クラスの非静的メンバ関数を指す関数ポインタ」は、関数呼び出し時にそのクラスのインスタンスを必要とする。デリゲートのようにオブジェクトとして扱うためには デリゲートにより、メソッド単位のコンポジション (合成) が可能となる。デリゲートは主に、イベント処理での活用(コールバック処理のカスタマイズ)を想定している。Javaなどでのインターフェイスを利用したイベント処理と比べ、デリゲートによって参照されるメソッド(実体)の名前を自由に宣言できる、振る舞いをカスタマイズするために(明示的な)インターフェイスの実装やスーパークラスの継承を行なう必要がない(新たにクラスをわざわざ定義しなくてよい[注釈 2])、などの利点がある。 C#では複数のデリゲートを そのほか、スレッドプールを利用して非同期でデリゲートを実行する機能も存在する(非同期デリゲート[7])。 Visual J++
参考
C#C#の例を示す。まず以下では、「 delegate int SomeDelegate(string p);
コンパイラによって、 次に、以下のようなメソッドを持つクラスがあったとする。 class SomeClass
{
private string someField = "TEST:";
public int SomeMethod(string p)
{
return this.someField.Length + p.Length;
}
public static int SomeStaticMethod(string p)
{
return p.Length;
}
}
そして、以下のように(デリゲートと同じ引数と戻り値を持つ)既存の名前付きメソッドを参照するデリゲート型オブジェクトを生成することができる。 SomeDelegate del = new SomeDelegate(SomeClass.SomeStaticMethod);
C/C++の関数ポインタと異なり、静的メソッドだけでなくインスタンスメソッドをデリゲートに代入することもできる(メソッドとインスタンスのペアがカプセル化される)。 SomeClass obj = new SomeClass();
SomeDelegate del = new SomeDelegate(obj.SomeMethod);
C# 2.0以降では、匿名メソッド (anonymous method) としてインラインで記述したメソッドを参照するデリゲートを同時に定義することもできる。この書き方では、 SomeDelegate del = new SomeDelegate(delegate(string p) {
return p.Length;
});
なおC# 3.0以降では、匿名メソッドの代わりにラムダ式を使って下記のように記述することもできる。 SomeDelegate del = (p) => {
return p.Length;
};
匿名メソッドとラムダ式は併せて匿名関数 (anonymous function) と呼ばれる。匿名関数は外側の変数にアクセスすることができ、これをキャプチャと呼ぶ。匿名関数とデリゲートを用いてクロージャを実現することができる。 こうして生成したデリゲート型オブジェクトは、通常のメソッドのように直接実行することができる。 int ret = del("some string");
なお、上記は以下の int ret = del.Invoke("some string");
しかし、デリゲートの真価が発揮されるのはイベントと併用した時である。イベントは、次のように宣言する。 class MyClass
{
public event SomeDelegate SomeEvent;
}
こうして宣言したイベントには、 MyClass obj = new MyClass();
obj.SomeEvent += new SomeDelegate(SomeEventHandler1);
obj.SomeEvent -= new SomeDelegate(SomeEventHandler1);
//obj.SomeEvent = new SomeDelegate(SomeEventHandler1); // 外部からの再代入はできないのでコンパイルエラー。
//obj.SomeEvent(); // 外部からの呼び出しはできないのでコンパイルエラー。
イベントハンドラの追加が 次のようにしてイベントの定義されたクラスの内部からイベントを起こすと、登録したイベントハンドラがまとめて実行される。 class MyClass
{
void ExecuteEventHandlers()
{
if (SomeEvent != null)
SomeEvent("some string");
}
}
実行される順番は登録順とは関係なく、未定義である。 なお、 P/Invokeとデリゲートプラットフォーム呼び出し(P/Invoke)の際は、デリゲートは既定でアンマネージ(ネイティブ)の関数ポインタにマーシャリングされる[8]。 例えば下記のようなC言語関数がMyLibrary.DLLに実装されているとする。 typedef BOOL (__stdcall*TMyCallbackFuncPtr)(LPCWSTR fileName);
/* カレントディレクトリのファイルを列挙する関数。ファイルが見つかるごとにコールバック関数が呼ばれる。 */
extern __declspec(dllexport) BOOL EnumFiles(TMyCallbackFuncPtr callback);
対応するP/Invokeラッパーインターフェイスおよび呼び出しの一例は下記のようになる。 using System;
using System.Runtime.InteropServices;
static class MyPInvoker
{
public delegate bool MyCallbackDelegate([In, MarshalAs(UnmanagedType.LPWStr)]string fileName);
[DllImport("MyLibrary.dll")]
public extern static bool EnumFiles(MyCallbackDelegate callback);
private static bool MyCallbackMethod(string fileName)
{
Console.WriteLine(fileName);
return true; // 列挙を続行。
}
private static void Test()
{
EnumFiles(MyCallbackMethod);
}
}
P/Invokeでデリゲートを渡す場合は、デリゲート型のインスタンスがガベージコレクションにより回収されてしまわないように注意する必要がある[9][10]。また、コールバック関数の呼び出し規約が一致するようにしなければならない(.NET 1.1まではstdcall呼び出し規約のみが使用可能だったが、.NET 2.0以降ではUnmanagedFunctionPointerAttribute属性を明示的に指定することでcdecl呼び出し規約を使用することも可能である[11])。 その他の言語のデリゲートD言語には関数オブジェクトがあり、型としてfunctionとdelegateがある。無名関数を作る式として関数リテラルがあり、functionとdelegateのそれぞれに対応した構文がある。関数リテラルの省略構文としてラムダ式がある。functionとdelegateの違いは、作られたスコープの環境にアクセスできるかどうかで、アクセスする場合はデリゲートである必要がある。ラムダ式では内容に応じて、デリゲートである必要がある場合はデリゲートになる。 Javaはバージョン8にてラムダ式とともにメソッド参照の機能を導入した。ただしマルチキャストデリゲートに相当する機能はない。 C++/CLIは.NETマネージ言語であり、.NETのデリゲートをサポートする。メソッド宣言に C++/CXは.NETマネージ言語ではなくネイティブ言語拡張だが、デリゲートをサポートする。イベントハンドラーの割り当てに利用される。ただし、C++/CXは参照カウントベースのガベージコレクションを採用していることから、強い参照による循環参照を防ぐため、イベントハンドラーの記述にはラムダ式よりも名前付き関数を利用することが推奨されている[14]。 Objective-Cは言語機能としてデリゲートを持たないが、Objective-Cを用いたイベント駆動型ソフトウェアを開発する際の基本的なデザインパターンとして「委譲」が採用されている[15]。実態はJavaのインターフェイスを利用したイベントコールバックと同じく、 →詳細は「委譲」を参照
脚注注釈
出典
関連項目 |