委派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.DelegateA delegate declaration defines a class that is derived from the class System.Delegate. 委派實例會封裝調用清單,這是一或多個方法的清單,每個方法都稱為可呼叫實體。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.

publicprotected 、和修飾詞 internal 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).

委派的類型名稱為 identifierThe 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 (variant 型別參數清單) 指定委派本身的型別參數。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. 此外,任何 outref 參數類型也必須是輸出安全的。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. 具體來說,具有相同參數清單和傳回類型的兩個不同委派類型會被視為不同的委派類型。Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types. 不過,兩個相異但結構相等的委派類型的實例,可能會與) 相等 (委派相等運算子 比較。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.M1 和都與委派型別和都 B.M1 相容 D1 D2 ,因為它們具有相同的傳回型別和參數清單; 不過,這些委派型別是兩種不同的型別,因此它們不是可互換的類型。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_declarationThe only way to declare a delegate type is via a delegate_declaration. 委派型別是衍生自的類別型別 System.DelegateA 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.DelegateIt 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. 建立委派實例時 (從單一方法 委派相容性) ,它會封裝該方法,而且其調用清單只會包含一個專案。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 的委派實例合併時,它們的調用清單會串連--在 order 左運算元和右運算元中,以形成新的調用清單,其中包含兩個或多個專案。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
    }

}

cd1 和具 cd2 現化時,它們都會封裝一個方法。When cd1 and cd2 are instantiated, they each encapsulate one method. 當具 cd3 現化時,它會有兩個方法的調用清單, M1 以及 M2 順序中的。When cd3 is instantiated, it has an invocation list of two methods, M1 and M2, in that order. cd4的叫用清單 M1 會依序包含、 M2M1cd4's invocation list contains M1, M2, and M1, in that order. 最後, cd5 的調用清單 M1 會依序包含、、 M2 M1M1M2Finally, 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 D 如果下列所有條件都成立,則方法或委派會與委派類型相容:A method or delegate M is compatible with a delegate type D if all of the following are true:

  • DM 擁有相同數目的參數,且中的每個參數 D 都與 ref out 中的對應參數具有相同的或 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 MFor 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.
  • 針對每個 refout 參數,中的參數類型與 D 中的參數類型相同 MFor each ref or out parameter, the parameter type in D is the same as the parameter type in M.
  • 從傳回型別,到的傳回型別都有識別或隱含參考轉換 M DAn 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
  • 無法) 的目標物件 (, null 以及 delegate_creation_expression 中參考的實例方法,或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. 請記住,合併兩個委派,或從另一個委派移除時,會有一個新的委派結果及其本身的調用清單;結合或移除委派的調用清單會維持不變。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. 當叫用清單包含一個專案的非 null 委派實例叫用時,它會以指定的相同引數叫用一個方法,並傳回與所參考方法相同的值。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. 如果這類委派調用包含 (參考) 參數 的參考參數,則會發生每個方法調用,並參考相同的變數;叫用清單中的方法將對該變數進行的變更,會在調用清單中向下顯示。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.NullReferenceExceptionAttempting 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. 在此情況下,它只會在每次發生時叫用一次。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