委托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
. 委托实例封装一个调用列表,它是一个或多个方法的列表,其中每个方法都称为可调用实体。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.
public
、、 protected
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).
委托的类型名称为 标识符。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 (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. 此外,任何 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. 具体而言,具有相同参数列表和返回类型的两个不同的委托类型被视为不同的委托类型。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.M2
、 B.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. 创建委托实例时 (委托单一方法中的 兼容性) ,它会封装该方法,其调用列表只包含一个条目。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 委托实例组合在一起,则会将其调用列表串联在顺序左操作数和右操作数之间,以形成包含两个或多个条目的新调用列表。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
M2
M1
按该顺序包含、和。cd4
's invocation list contains M1
, M2
, and M1
, in that order. 最后, cd5
的调用列表 M1
M2
M1
M1
按该顺序包含、、、和 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
与委托类型 兼容 D
:A method or delegate M
is compatible with a delegate type D
if all of the following are true:
D
和M
具有相同数量的参数,并且中的每个参数D
ref
out
与中的相应参数具有相同的或修饰符M
。D
andM
have the same number of parameters, and each parameter inD
has the sameref
orout
modifiers as the corresponding parameter inM
.- 对于每个 value 参数 (没有
ref
或out
修饰符) 的参数, (标识转换) 或隐式引用转换 (存在从中的参数类型D
到中的相应参数类型M
。For each value parameter (a parameter with noref
orout
modifier), an identity conversion (Identity conversion) or implicit reference conversion (Implicit reference conversions) exists from the parameter type inD
to the corresponding parameter type inM
. - 对于每个
ref
或out
参数,中的参数类型与D
中的参数类型相同M
。For eachref
orout
parameter, the parameter type inD
is the same as the parameter type inM
. - 从的返回类型到的返回类型存在标识或隐式引用转换
M
D
。An identity or implicit reference conversion exists from the return type ofM
to the return type ofD
.
委托实例化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 benull
) 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.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. 在这种情况下,它只会在每次出现时调用一次。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