デリゲートの使用 (C# プログラミング ガイド)
デリゲートは、C や C++ の関数ポインターと同じように、メソッドを安全にカプセル化する型です。 デリゲートはオブジェクト指向であり、タイプ セーフであり、さらに安全である点が、C の関数ポインターと異なります。 デリゲートの型は、デリゲートの名前によって定義されます。 次の例では、文字列を引数として受け取り void を返すメソッドをカプセル化できる、Del という名前のデリゲートを宣言しています。
public delegate void Del(string message);
デリゲート オブジェクトは、通常、デリゲートがラップするメソッドの名前を指定することによって、または匿名メソッドを使用して構築されます。 デリゲートがインスタンス化されると、デリゲートに対して行われたメソッド呼び出しは、デリゲートによってそのメソッドに渡されます。 呼び出し元によってデリゲートに渡されたパラメーターはメソッドに渡され、メソッドからの戻り値 (存在する場合) は、デリゲートによって呼び出し元に返されます。 これをデリゲートの呼び出しと言います。 インスタンス化されたデリゲートは、ラップされたメソッドそのもののように呼び出すことができます。 次に例を示します。
// Create a method for a delegate.
public static void DelegateMethod(string message)
{
System.Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;
// Call the delegate.
handler("Hello World");
デリゲート型は、.NET Framework の Delegate クラスから派生します。 デリゲート型は sealed であるため派生できず、Delegate からカスタム クラスを派生させることはできません。 インスタンス化されたデリゲートはオブジェクトなので、パラメーターとして渡したり、プロパティに代入したりできます。 このため、メソッドはデリゲートをパラメーターとして受け入れ、後ほどそのデリゲートを呼び出すことができます。 これを非同期コールバックと言い、長いプロセスが完了したときに呼び出し元に通知するための一般的な方法です。 デリゲートをこのように使用する場合、デリゲートを使用するコードでは、使用されているメソッドの実装を認識する必要がありません。 この機能は、インターフェイスが提供するカプセル化に似ています。 詳細については、「インターフェイスの代わりにデリゲートを使用する場合 (C# プログラミング ガイド)」を参照してください。
これ以外に、コールバックは、カスタム比較メソッドを定義し、そのデリゲートを並べ替えメソッドに渡すためにもよく使用されます。 これにより、呼び出し元のコードを並べ替えアルゴリズムの一部にできます。 次のメソッドの例では、Del 型をパラメーターとして使用しています。
public void MethodWithCallback(int param1, int param2, Del callback)
{
callback("The number is: " + (param1 + param2).ToString());
}
上で作成したデリゲートは、次のようにメソッドに渡すことができます。
MethodWithCallback(1, 2, handler);
そして、次の出力をコンソールに送ります。
The number is: 3
デリゲートを抽象として使用すると、MethodWithCallback は、コンソールを直接呼び出す必要がないので、コンソールを念頭に入れて設計する必要がありません。 MethodWithCallback が実行することは、文字列を準備し、それを別のメソッドに渡すだけです。 このことが特に有効な理由は、デリゲート メソッドが任意の数のパラメーターを使用できるという事実にあります。
インスタンス メソッドをラップするようにデリゲートを構築すると、デリゲートは、インスタンスとメソッドの両方を参照します。 デリゲートは、インスタンス型がラップしているメソッド以外の情報を持たないので、デリゲート シグネチャに一致するメソッドがオブジェクトに存在する限り、あらゆる型のオブジェクトを参照できます。 静的メソッドをラップするようにデリゲートを構築すると、デリゲートは静的メソッドだけを参照します。 次に宣言の例を示します。
public class MethodClass
{
public void Method1(string message) { }
public void Method2(string message) { }
}
これで、上の静的 DelegateMethod と合わせて 3 つのメソッドを Del インスタンスによってラップできるようになりました。
デリゲートを呼び出すと、デリゲートは複数のメソッドを呼び出すことができます。 これをマルチキャスティングと言います。 デリゲートのメソッド リスト (呼び出しリスト) に新しいメソッドを追加するには、加算演算子 ("+") または加算代入演算子 ("+=") を使用して 2 つのデリゲートを加算します。 次に例を示します。
MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;
//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
この時点で、allMethodsDelegate の呼び出しリストには、Method1、Method2、DelegateMethod の 3 つのメソッドが含まれています。 元の 3 つのデリゲート (d1、d2、および d3) は変更されません。 allMethodsDelegate を呼び出すと、3 つのメソッドがすべて順に呼び出されます。 デリゲートで参照パラメーターを使用している場合、参照は、これら 3 つのメソッドに順に渡され、1 つのメソッドによって行われた変更が次のメソッドに対して可視になります。 これらのメソッドのいずれかが、メソッド内でキャッチされない例外をスローした場合、その例外はデリゲートの呼び出し元に渡され、呼び出しリスト内の後続のメソッドが呼び出されなくなります。 デリゲートに戻り値や out パラメーターが存在する場合は、最後に呼び出されたメソッドの戻り値とパラメーターを返します。 呼び出しリストからメソッドを削除するには、デクリメント演算子 ("-") またはデクリメント代入演算子 ("-=") を使用します。 次に例を示します。
//remove Method1
allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;
デリゲート型は System.Delegate から派生するため、このクラスで定義されているメソッドとプロパティをデリゲートで呼び出すことができます。 たとえば、デリゲートの呼び出しリストに含まれるメソッドの数を確認する場合は、次のようなコードを記述できます。
int invocationCount = d1.GetInvocationList().GetLength(0);
呼び出しリストに複数のメソッドを含むデリゲートは、System.Delegate のサブクラスである MulticastDelegate から派生します。 これらのクラスは共に GetInvocationList をサポートするので、上のコードはどちらの場合にも有効です。
マルチキャスト デリゲートは、イベント処理で広く使用されています。 イベント ソース オブジェクトは、特定のイベントの受け取り先として登録されている受け取り側のオブジェクトにイベント通知を送信します。 イベントに対して登録するには、受け取り側でイベントを処理できるメソッドを作成し、そのメソッドのデリゲートを作成して、イベント ソースにデリゲートを渡します。 イベントが発生すると、ソースがデリゲートを呼び出します。 デリゲートは、受け取り側のイベント処理メソッドを呼び出し、イベント データを伝達します。 特定のイベントのデリゲート型は、イベント ソースによって定義されます。 詳細については、「イベント (C# プログラミング ガイド)」を参照してください。
2 つの異なる型が代入されたデリゲートをコンパイル時に比較すると、コンパイル エラーになります。 デリゲート インスタンスが静的な System.Delegate 型の場合、比較は可能ですが、実行時に false を返します。 次に例を示します。
delegate void Delegate1();
delegate void Delegate2();
static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
// Compile-time error.
//Console.WriteLine(d == e);
// OK at compile-time. False if the run-time type of f
// is not the same as that of d.
System.Console.WriteLine(d == f);
}
参照
参照
デリゲートの分散の使用 (C# および Visual Basic)
Func および Action 汎用デリゲートでの分散の使用 (C# および Visual Basic)