System.Delegate と
この記事では、デリゲートをサポートする .NET のクラスと、それらのクラスを delegate
キーワードにマップする方法について説明します。
デリゲート型を定義する
'delegate' キーワードから始めましょう。これは主にデリゲートを操作するときに使用するためです。
delegate
キーワードを使用するときにコンパイラによって生成されるコードは、DelegateクラスおよびMulticastDelegate クラスのメンバーを呼び出すメソッド呼び出しにマップされます。
メソッドシグネチャの定義に似た構文を使用してデリゲート型を定義します。
delegate
キーワードを定義に追加するだけです。
引き続き List.Sort() メソッドを例として使用してみましょう。 最初の手順では、比較デリゲートの型を作成します。
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
コンパイラは、使用されるシグネチャに一致する System.Delegate
から派生したクラスを生成します (この場合は、整数を返し、2 つの引数を持つメソッド)。 そのデリゲートの型は Comparison
。
Comparison
デリゲート型はジェネリック型です。 詳細については、「 ジェネリック クラスとメソッド」を参照してください。
構文は変数を宣言しているかのように見えるかもしれませんが、実際には型を宣言していることに注意 してください。 デリゲート型は、クラス内、名前空間内、またはグローバル名前空間内で直接定義できます。
注
グローバル名前空間でデリゲート型 (またはその他の型) を直接宣言することはお勧めしません。
コンパイラでは、この新しい型の追加ハンドラーと削除ハンドラーも生成されるため、このクラスのクライアントはインスタンスの呼び出しリストからメソッドを追加および削除できます。 コンパイラは、追加または削除されるメソッドのシグネチャが、メソッドの宣言時に使用されるシグネチャと一致することを強制します。
デリゲートのインスタンスを宣言する
デリゲートを定義したら、その型のインスタンスを作成できます。 C# のすべての変数と同様に、デリゲート インスタンスを名前空間またはグローバル名前空間で直接宣言することはできません。
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
変数の型は、前に定義したデリゲート型 Comparison<T>
。 変数の名前は comparator
。
上記のコード スニペットは、クラス内でメンバー変数を宣言しました。 ローカル変数であるデリゲート変数、またはメソッドへの引数を宣言することもできます。
デリゲートを呼び出す
デリゲートの呼び出しリストにあるメソッドを呼び出すには、そのデリゲートを呼び出します。
Sort()
メソッド内で、コードは比較メソッドを呼び出して、オブジェクトを配置する順序を決定します。
int result = comparator(left, right);
上記の行では、デリゲートにアタッチされているメソッドが 呼び出 されます。 変数はメソッド名として扱い、通常のメソッド呼び出し構文を使用して呼び出します。
そのコード行は安全でない前提になります。ターゲットがデリゲートに追加されたという保証はありません。 ターゲットがアタッチされていない場合、上記の行によって NullReferenceException
がスローされます。 この問題に対処するために使用されるイディオムは、単純な null チェックよりも複雑であり、この シリーズの後半で説明します。
呼び出しターゲットの割り当て、追加、および削除
デリゲート型の定義方法と、デリゲート インスタンスの宣言と呼び出し方法です。
List.Sort()
メソッドを使用する開発者は、シグネチャがデリゲート型定義と一致するメソッドを定義し、並べ替えメソッドで使用されるデリゲートに割り当てる必要があります。 この割り当てにより、そのデリゲート オブジェクトの呼び出しリストにメソッドが追加されます。
文字列のリストを長さで並べ替えたいとします。 比較関数は次のようになります。
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
メソッドはプライベート メソッドとして宣言されます。 それでかまいません。 このメソッドをパブリック インターフェイスの一部にしたくない場合があります。 デリゲートにアタッチされている場合でも、比較メソッドとして使用できます。 呼び出し元のコードでは、このメソッドがデリゲート オブジェクトのターゲット リストにアタッチされ、そのデリゲートを介してアクセスできます。
そのリレーションシップを作成するには、そのメソッドを List.Sort()
メソッドに渡します。
phrases.Sort(CompareLength);
メソッド名がかっこなしで使用されていることに注意してください。 メソッドを引数として使用すると、メソッド参照をデリゲート呼び出しターゲットとして使用できる参照に変換し、そのメソッドを呼び出しターゲットとしてアタッチするようにコンパイラに指示します。
Comparison<string>
型の変数を宣言し、代入を行うことで、明示的にすることもできます。
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
デリゲート ターゲットとして使用されるメソッドが小さなメソッドである場合は、 ラムダ式 構文を使用して代入を実行するのが一般的です。
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
デリゲート ターゲットにラムダ式を使用する方法については、 後のセクションで詳しく説明します。
Sort() の例では、通常、1 つのターゲット メソッドをデリゲートにアタッチします。 ただし、デリゲート オブジェクトは、デリゲート オブジェクトにアタッチされた複数のターゲット メソッドを持つ呼び出しリストをサポートします。
デリゲート クラスと MulticastDelegate クラス
上記の言語サポートは、通常、デリゲートを操作するために必要な機能とサポートを提供します。 これらの機能は、.NET Core フレームワークの 2 つのクラス ( Delegate と MulticastDelegate) に基づいて構築されています。
System.Delegate
クラスとその 1 つの直接サブクラス (System.MulticastDelegate
) は、デリゲートの作成、デリゲート ターゲットとしてのメソッドの登録、およびデリゲート ターゲットとして登録されているすべてのメソッドの呼び出しをサポートするフレームワークを提供します。
興味深いことに、 System.Delegate
クラスと System.MulticastDelegate
クラス自体はデリゲート型ではありません。 これらは、すべての特定のデリゲート型の基礎を提供します。 同じ言語設計プロセスでは、 Delegate
または MulticastDelegate
から派生するクラスを宣言できないことが義務付けられています。 C# 言語規則では禁止されています。
代わりに、C# コンパイラは、C# 言語キーワードを使用してデリゲート型を宣言するときに、 MulticastDelegate
から派生したクラスのインスタンスを作成します。
この設計は、C# と .NET の最初のリリースに根ざしています。 設計チームの目標の 1 つは、デリゲートを使用するときに言語によって型の安全性が強制されるようにすることでした。 これは、デリゲートが適切な型と引数の数で呼び出されたことを保証することを意味しました。 また、すべての戻り値の型がコンパイル時に正しく示されていること。 デリゲートは、ジェネリックより前の 1.0 .NET リリースの一部でした。
この型の安全性を強制する最善の方法は、使用されているメソッド シグネチャを表す具体的なデリゲート クラスをコンパイラが作成することでした。
派生クラスを直接作成することはできませんが、これらのクラスで定義されているメソッドを使用します。 デリゲートを操作するときに使用する最も一般的な方法を見てみましょう。
最初に覚えておくべき最も重要な事実は、作業するすべてのデリゲートが MulticastDelegate
から派生していることです。 マルチキャスト デリゲートは、デリゲートを介して呼び出すときに、複数のメソッド ターゲットを呼び出すことができることを意味します。 元の設計では、1 つのターゲット メソッドのみをアタッチして呼び出すことができるデリゲートと、複数のターゲット メソッドをアタッチして呼び出すことができるデリゲートを区別することを検討しました。 その区別は、最初の考えよりも実際にはあまり役に立たないことを証明しました。 2 つの異なるクラスは既に作成されており、最初のパブリック リリース以降にフレームワークに含まれています。
デリゲートで最も多く使用するメソッドは、 Invoke()
と BeginInvoke()
/ EndInvoke()
です。
Invoke()
は、特定のデリゲート インスタンスにアタッチされているすべてのメソッドを呼び出します。 前述のように、通常はデリゲート変数のメソッド呼び出し構文を使用してデリゲートを呼び出します。
このシリーズの後半で説明するように、これらのメソッドで直接動作するパターンがあります。
言語構文とデリゲートをサポートするクラスを確認したので、厳密に型指定されたデリゲートが使用、作成、および呼び出される方法を調べてみましょう。
.NET