デリゲートDelegates

デリゲートを使用するとC++、、Pascal、Modula などの他の言語が関数ポインターでアドレス指定されるシナリオを実現できます。Delegates enable scenarios that other languages—such as C++, Pascal, and Modula -- have addressed with function pointers. ただしC++ 、関数ポインターとは異なり、デリゲートは完全なオブジェクト指向C++であり、メンバー関数へのポインターとは異なり、デリゲートはオブジェクトインスタンスとメソッドの両方をカプセル化します。Unlike C++ function pointers, however, delegates are fully object oriented, and unlike C++ pointers to member functions, delegates encapsulate both an object instance and a method.

デリゲート宣言は、System.Delegateクラスから派生したクラスを定義します。A delegate declaration defines a class that is derived from the class System.Delegate. デリゲートインスタンスは、1つまたは複数のメソッドのリストである呼び出しリストをカプセル化します。各メソッドは、呼び出し可能なエンティティと呼ばれます。A delegate instance encapsulates an invocation list, which is a list of one or more methods, each of which is referred to as a callable entity. インスタンスメソッドの場合、呼び出し可能なエンティティは、インスタンスと、そのインスタンスのメソッドで構成されます。For instance methods, a callable entity consists of an instance and a method on that instance. 静的メソッドの場合、呼び出し可能なエンティティはメソッドだけで構成されます。For static methods, a callable entity consists of just a method. 適切な引数のセットを使用してデリゲートインスタンスを呼び出すと、指定された一連の引数を使用してデリゲートの呼び出し可能なエンティティが呼び出されます。Invoking a delegate instance with an appropriate set of arguments causes each of the delegate's callable entities to be invoked with the given set of arguments.

デリゲートインスタンスの興味深い便利なプロパティは、カプセル化するメソッドのクラスについて関知しないことです。これらのメソッドは、デリゲートの型と互換性がある (デリゲート宣言) ことだけが重要です。An interesting and useful property of a delegate instance is that it does not know or care about the classes of the methods it encapsulates; all that matters is that those methods be compatible (Delegate declarations) with the delegate's type. これにより、デリゲートは "匿名" 呼び出しに適しています。This makes delegates perfectly suited for "anonymous" invocation.

デリゲート宣言Delegate declarations

Delegate_declarationは、新しいデリゲート型を宣言するtype_declaration (型宣言) です。A delegate_declaration is a type_declaration (Type declarations) that declares a new delegate type.

delegate_declaration
    : attributes? delegate_modifier* 'delegate' return_type
      identifier variant_type_parameter_list?
      '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
    ;

delegate_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | delegate_modifier_unsafe
    ;

デリゲート宣言で同じ修飾子が複数回出現する場合、コンパイル時エラーになります。It is a compile-time error for the same modifier to appear multiple times in a delegate declaration.

new 修飾子は、別の型で宣言されたデリゲートに対してのみ許可されます。この場合、新しい修飾子で説明されているように、このようなデリゲートは、継承されたメンバーを同じ名前で隠ぺいすることを指定します。The new modifier is only permitted on delegates declared within another type, in which case it specifies that such a delegate hides an inherited member by the same name, as described in The new modifier.

publicprotectedinternal、および private 修飾子は、デリゲート型のアクセシビリティを制御します。The public, protected, internal, and private modifiers control the accessibility of the delegate type. デリゲート宣言が発生したコンテキストによっては、これらの修飾子の一部が許可されない場合があります (アクセシビリティの宣言)。Depending on the context in which the delegate declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

デリゲートの型名はidentifierです。The delegate's type name is identifier.

省略可能なformal_parameter_listは、デリゲートのパラメーターを指定し、 return_typeデリゲートの戻り値の型を示します。The optional formal_parameter_list specifies the parameters of the delegate, and return_type indicates the return type of the delegate.

省略可能なvariant_type_parameter_list (バリアント型パラメーターリスト) は、デリゲート自体の型パラメーターを指定します。The optional variant_type_parameter_list (Variant type parameter lists) specifies the type parameters to the delegate itself.

デリゲート型の戻り値の型は voidであるか、または出力セーフ (差異の安全性) である必要があります。The return type of a delegate type must be either void, or output-safe (Variance safety).

デリゲート型のすべての仮パラメーター型は、入力セーフである必要があります。All the formal parameter types of a delegate type must be input-safe. また、out または ref パラメーター型も、出力セーフである必要があります。Additionally, any out or ref parameter types must also be output-safe. 基になる実行プラットフォームの制限により、out パラメーターも入力セーフである必要があることに注意してください。Note that even out parameters are required to be input-safe, due to a limitation of the underlying execution platform.

のC#デリゲート型は同じ名前であり、構造的に同等ではありません。Delegate types in C# are name equivalent, not structurally equivalent. 具体的には、同じパラメーターリストと戻り値の型を持つ2つの異なるデリゲート型は、異なるデリゲート型と見なされます。Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types. ただし、2つの異なるが構造的に等価なデリゲート型のインスタンスは、等しいと見なされる場合があります (デリゲート等値演算子)。However, instances of two distinct but structurally equivalent delegate types may compare as equal (Delegate equality operators).

例 :For example:

delegate int D1(int i, double d);

class A
{
    public static int M1(int a, double b) {...}
}

class B
{
    delegate int D2(int c, double d);
    public static int M1(int f, double g) {...}
    public static void M2(int k, double l) {...}
    public static int M3(int g) {...}
    public static void M4(int g) {...}
}

A.M1B.M1 のメソッドは、同じ戻り値の型とパラメーターリストを持つため、D1D2 の両方のデリゲート型と互換性があります。ただし、これらのデリゲート型は2つの異なる型であるため、交換することはできません。The methods A.M1 and B.M1 are compatible with both the delegate types D1 and D2 , since they have the same return type and parameter list; however, these delegate types are two different types, so they are not interchangeable. メソッド B.M2B.M3、および B.M4 は、異なる戻り値の型またはパラメーターリストを持っているため D1 および D2のデリゲート型と互換性がありません。The methods B.M2, B.M3, and B.M4 are incompatible with the delegate types D1 and D2, since they have different return types or parameter lists.

他のジェネリック型宣言と同様に、構築されたデリゲート型を作成するには、型引数を指定する必要があります。Like other generic type declarations, type arguments must be given to create a constructed delegate type. 構築されたデリゲート型のパラメーターの型と戻り値の型は、デリゲート宣言の型パラメーターごとに、構築されたデリゲート型の対応する型引数に代入することによって作成されます。The parameter types and return type of a constructed delegate type are created by substituting, for each type parameter in the delegate declaration, the corresponding type argument of the constructed delegate type. 結果の戻り値の型とパラメーターの型は、構築されたデリゲート型と互換性のあるメソッドを決定するために使用されます。The resulting return type and parameter types are used in determining what methods are compatible with a constructed delegate type. 例 :For example:

delegate bool Predicate<T>(T value);

class X
{
    static bool F(int i) {...}
    static bool G(string s) {...}
}

メソッド X.F はデリゲート型 Predicate<int> と互換性があり、メソッド X.G はデリゲート型 Predicate<string> と互換性があります。The method X.F is compatible with the delegate type Predicate<int> and the method X.G is compatible with the delegate type Predicate<string> .

デリゲート型を宣言する唯一の方法は、 delegate_declarationを使用することです。The only way to declare a delegate type is via a delegate_declaration. デリゲート型は、System.Delegateから派生したクラス型です。A delegate type is a class type that is derived from System.Delegate. デリゲート型は暗黙的に sealedため、デリゲート型から型を派生させることはできません。Delegate types are implicitly sealed, so it is not permissible to derive any type from a delegate type. また、System.Delegateから非デリゲートクラス型を派生させることもできません。It is also not permissible to derive a non-delegate class type from System.Delegate. System.Delegate はそれ自体がデリゲート型ではないことに注意してください。これは、すべてのデリゲート型の派生元であるクラス型です。Note that System.Delegate is not itself a delegate type; it is a class type from which all delegate types are derived.

C#デリゲートのインスタンス化と呼び出しのための特殊な構文を提供します。C# provides special syntax for delegate instantiation and invocation. インスタンス化を除き、クラスまたはクラスのインスタンスに適用できる操作は、それぞれデリゲートクラスまたはインスタンスに適用することもできます。Except for instantiation, any operation that can be applied to a class or class instance can also be applied to a delegate class or instance, respectively. 特に、通常のメンバーアクセス構文を使用して System.Delegate 型のメンバーにアクセスすることができます。In particular, it is possible to access members of the System.Delegate type via the usual member access syntax.

デリゲートインスタンスによってカプセル化されるメソッドのセットは、呼び出しリストと呼ばれます。The set of methods encapsulated by a delegate instance is called an invocation list. デリゲートインスタンスが1つのメソッドから作成 (デリゲート互換性) されると、そのメソッドがカプセル化され、その呼び出しリストにはエントリが1つだけ含まれます。When a delegate instance is created (Delegate compatibility) from a single method, it encapsulates that method, and its invocation list contains only one entry. ただし、null 以外の2つのデリゲートインスタンスを結合すると、2つ以上のエントリを含む新しい呼び出しリストを形成するために、その呼び出しリストが連結されます。However, when two non-null delegate instances are combined, their invocation lists are concatenated -- in the order left operand then right operand -- to form a new invocation list, which contains two or more entries.

デリゲートは、バイナリ + (加算演算子) と += 演算子 (複合代入) を使用して結合されます。Delegates are combined using the binary + (Addition operator) and += operators (Compound assignment). デリゲートは、二項 - (減算演算子) と -= 演算子 (複合代入) を使用して、デリゲートの組み合わせから削除できます。A delegate can be removed from a combination of delegates, using the binary - (Subtraction operator) and -= operators (Compound assignment). デリゲートは、等価性 (デリゲート等値演算子) を比較できます。Delegates can be compared for equality (Delegate equality operators).

次の例は、さまざまなデリゲートのインスタンス化と、それらに対応する呼び出しリストを示しています。The following example shows the instantiation of a number of delegates, and their corresponding invocation lists:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public static void M2(int i) {...}

}

class Test
{
    static void Main() {
        D cd1 = new D(C.M1);      // M1
        D cd2 = new D(C.M2);      // M2
        D cd3 = cd1 + cd2;        // M1 + M2
        D cd4 = cd3 + cd1;        // M1 + M2 + M1
        D cd5 = cd4 + cd3;        // M1 + M2 + M1 + M1 + M2
    }

}

cd1cd2 がインスタンス化されると、それぞれが1つのメソッドをカプセル化します。When cd1 and cd2 are instantiated, they each encapsulate one method. cd3 がインスタンス化されると、その順序で M1M2の2つのメソッドの呼び出しリストがあります。When cd3 is instantiated, it has an invocation list of two methods, M1 and M2, in that order. cd4の呼び出しリストには、M1M2、および M1がこの順序で含まれています。cd4's invocation list contains M1, M2, and M1, in that order. 最後に、cd5の呼び出しリストには、M1M2M1M1、および M2がこの順序で含まれています。Finally, cd5's invocation list contains M1, M2, M1, M1, and M2, in that order. デリゲートの結合 (および削除) のその他の例については、「デリゲートの呼び出し」を参照してください。For more examples of combining (as well as removing) delegates, see Delegate invocation.

デリゲートの互換性Delegate compatibility

次のすべての条件を満たす場合、メソッドまたはデリゲート M はデリゲート型と互換性があり DA method or delegate M is compatible with a delegate type D if all of the following are true:

  • DM は同じ数のパラメーターを持ち、D 内の各パラメーターには、out の対応するパラメーターと同じ ref または Mの修飾子があります。D and M have the same number of parameters, and each parameter in D has the same ref or out modifiers as the corresponding parameter in M.
  • 値パラメーター (ref または out 修飾子のないパラメーター) については、D のパラメーターの型から M内の対応するパラメーターの型に、id 変換 (id 変換) または暗黙の参照変換 (暗黙的な参照変換) が存在します。For each value parameter (a parameter with no ref or out modifier), an identity conversion (Identity conversion) or implicit reference conversion (Implicit reference conversions) exists from the parameter type in D to the corresponding parameter type in M.
  • ref または out パラメーターごとに、D のパラメーターの型は Mのパラメーターの型と同じになります。For each ref or out parameter, the parameter type in D is the same as the parameter type in M.
  • M の戻り値の型から Dの戻り値の型に、id または暗黙的な参照変換が存在します。An identity or implicit reference conversion exists from the return type of M to the return type of D.

デリゲートのインスタンス化Delegate instantiation

デリゲートのインスタンスは、 delegate_creation_expression (デリゲート作成式) またはデリゲート型への変換によって作成されます。An instance of a delegate is created by a delegate_creation_expression (Delegate creation expressions) or a conversion to a delegate type. 新しく作成されたデリゲートインスタンスは、次のいずれかを参照します。The newly created delegate instance then refers to either:

  • Delegate_creation_expressionで参照されている静的メソッド。The static method referenced in the delegate_creation_expression, or
  • Delegate_creation_expressionで参照されている (nullできない) ターゲットオブジェクトとインスタンスメソッド。The target object (which cannot be null) and instance method referenced in the delegate_creation_expression, or
  • 別のデリゲート。Another delegate.

例 :For example:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public void M2(int i) {...}
}

class Test
{
    static void Main() { 
        D cd1 = new D(C.M1);        // static method
        C t = new C();
        D cd2 = new D(t.M2);        // instance method
        D cd3 = new D(cd2);        // another delegate
    }
}

インスタンス化されると、デリゲートインスタンスは常に同じターゲットオブジェクトとメソッドを参照します。Once instantiated, delegate instances always refer to the same target object and method. 2つのデリゲートが結合されている場合、または1つが別のデリゲートから削除された場合は、新しいデリゲートの結果が独自の呼び出しリストになります。結合または削除されたデリゲートの呼び出しリストは変更されません。Remember, when two delegates are combined, or one is removed from another, a new delegate results with its own invocation list; the invocation lists of the delegates combined or removed remain unchanged.

デリゲートの呼び出しDelegate invocation

C#デリゲートを呼び出すための特別な構文を提供します。C# provides special syntax for invoking a delegate. 呼び出しリストに1つのエントリが含まれている null 以外のデリゲートインスタンスが呼び出されると、指定されたのと同じ引数を使用して1つのメソッドが呼び出され、参照先のメソッドと同じ値が返されます。When a non-null delegate instance whose invocation list contains one entry is invoked, it invokes the one method with the same arguments it was given, and returns the same value as the referred to method. (デリゲート呼び出しの詳細については、「デリゲートの呼び出し」を参照してください)。このようなデリゲートの呼び出し中に例外が発生し、その例外が呼び出されたメソッド内でキャッチされなかった場合、そのデリゲートを呼び出したメソッドがそのメソッドに直接呼び出されたかのように、例外の catch 句の検索はデリゲートを呼び出したメソッドで続行されます。(See Delegate invocations for detailed information on delegate invocation.) If an exception occurs during the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, as if that method had directly called the method to which that delegate referred.

呼び出しリストに複数のエントリが含まれているデリゲートインスタンスの呼び出しは、呼び出しリスト内の各メソッドを同期的に順番に呼び出すことによって続行されます。Invocation of a delegate instance whose invocation list contains multiple entries proceeds by invoking each of the methods in the invocation list, synchronously, in order. を呼び出す各メソッドには、デリゲートインスタンスに指定されたものと同じ引数セットが渡されます。Each method so called is passed the same set of arguments as was given to the delegate instance. このようなデリゲート呼び出しに参照パラメーター (参照パラメーター) が含まれている場合、各メソッド呼び出しは同じ変数への参照を使用して発生します。呼び出しリストの1つのメソッドによってその変数に加えられた変更は、呼び出しリストの下位にあるメソッドから参照できます。If such a delegate invocation includes reference parameters (Reference parameters), each method invocation will occur with a reference to the same variable; changes to that variable by one method in the invocation list will be visible to methods further down the invocation list. デリゲート呼び出しに出力パラメーターまたは戻り値が含まれている場合、最終的な値はリスト内の最後のデリゲートの呼び出しから取得されます。If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list.

このようなデリゲートの呼び出しの処理中に例外が発生し、呼び出されたメソッド内で例外がキャッチされない場合は、デリゲートを呼び出したメソッドで例外の catch 句を検索し、すべてのメソッドをさらに下に移動します。呼び出しリストは呼び出されません。If an exception occurs during processing of the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, and any methods further down the invocation list are not invoked.

値が null であるデリゲートインスタンスを呼び出そうとすると、System.NullReferenceException型の例外が発生します。Attempting to invoke a delegate instance whose value is null results in an exception of type System.NullReferenceException.

次の例は、デリゲートのインスタンス化、結合、削除、および呼び出しを行う方法を示しています。The following example shows how to instantiate, combine, remove, and invoke delegates:

using System;

delegate void D(int x);

class C
{
    public static void M1(int i) {
        Console.WriteLine("C.M1: " + i);
    }

    public static void M2(int i) {
        Console.WriteLine("C.M2: " + i);
    }

    public void M3(int i) {
        Console.WriteLine("C.M3: " + i);
    }
}

class Test
{
    static void Main() { 
        D cd1 = new D(C.M1);
        cd1(-1);                // call M1

        D cd2 = new D(C.M2);
        cd2(-2);                // call M2

        D cd3 = cd1 + cd2;
        cd3(10);                // call M1 then M2

        cd3 += cd1;
        cd3(20);                // call M1, M2, then M1

        C c = new C();
        D cd4 = new D(c.M3);
        cd3 += cd4;
        cd3(30);                // call M1, M2, M1, then M3

        cd3 -= cd1;             // remove last M1
        cd3(40);                // call M1, M2, then M3

        cd3 -= cd4;
        cd3(50);                // call M1 then M2

        cd3 -= cd2;
        cd3(60);                // call M1

        cd3 -= cd2;             // impossible removal is benign
        cd3(60);                // call M1

        cd3 -= cd1;             // invocation list is empty so cd3 is null

        cd3(70);                // System.NullReferenceException thrown

        cd3 -= cd1;             // impossible removal is benign
    }
}

ステートメント cd3 += cd1;に示すように、デリゲートは呼び出しリスト内に複数回存在できます。As shown in the statement cd3 += cd1;, a delegate can be present in an invocation list multiple times. この場合、単に1回だけ呼び出されます。In this case, it is simply invoked once per occurrence. このような呼び出しリストでは、そのデリゲートが削除されると、呼び出しリストで最後に出現したものが実際に削除されたものになります。In an invocation list such as this, when that delegate is removed, the last occurrence in the invocation list is the one actually removed.

最後のステートメントを実行する直前に、cd3 -= cd1;、デリゲート cd3 は空の呼び出しリストを参照します。Immediately prior to the execution of the final statement, cd3 -= cd1;, the delegate cd3 refers to an empty invocation list. 空のリストからデリゲートを削除しようとしている (または、空でないリストから存在しないデリゲートを削除する) と、エラーにはなりません。Attempting to remove a delegate from an empty list (or to remove a non-existent delegate from a non-empty list) is not an error.

生成される出力は次のとおりです。The output produced is:

C.M1: -1
C.M2: -2
C.M1: 10
C.M2: 10
C.M1: 20
C.M2: 20
C.M1: 20
C.M1: 30
C.M2: 30
C.M1: 30
C.M3: 30
C.M1: 40
C.M2: 40
C.M3: 40
C.M1: 50
C.M2: 50
C.M1: 60
C.M1: 60