System.Delegate 和 delegate 关键字System.Delegate and the delegate keyword


本文介绍 .NET 中支持委托的类以及这些类映射到 delegate 关键字的方式。This article covers the classes in .NET that support delegates, and how those map to the delegate keyword.

定义委托类型Define delegate types

我们从“delegate”关键字开始,因为这是你在使用委托时会使用的主要方法。Let's start with the 'delegate' keyword, because that's primarily what you will use as you work with delegates. 编译器在你使用 delegate 关键字时生成的代码会映射到调用 DelegateMulticastDelegate 类的成员的方法调用。The code that the compiler generates when you use the delegate keyword will map to method calls that invoke members of the Delegate and MulticastDelegate classes.

可使用类似于定义方法签名的语法来定义委托类型。You define a delegate type using syntax that is similar to defining a method signature. 只需向定义添加 delegate 关键字即可。You just add the delegate keyword to the definition.

我们继续使用 List.Sort() 方法作为示例。Let's continue to use the List.Sort() method as our example. 第一步是为比较委托创建类型:The first step is to create a type for the comparison delegate:

// From the .NET Core library

// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);

编译器会生成一个类,它派生自与使用的签名匹配的 System.Delegate(在此例中,是返回一个整数并具有两个参数的方法)。The compiler generates a class, derived from System.Delegate that matches the signature used (in this case, a method that returns an integer, and has two arguments). 该委托的类型是 ComparisonThe type of that delegate is Comparison. Comparison 委托类型是泛型类型。The Comparison delegate type is a generic type. 有关泛型的详细信息,请参阅此处For details on generics see here.

请注意,语法可能看起来像是声明变量,但它实际上是声明类型 。Notice that the syntax may appear as though it is declaring a variable, but it is actually declaring a type. 可以在类中、直接在命名空间中、甚至是在全局命名空间中定义委托类型。You can define delegate types inside classes, directly inside namespaces, or even in the global namespace.


建议不要直接在全局命名空间中声明委托类型(或其他类型)。Declaring delegate types (or other types) directly in the global namespace is not recommended.

编译器还会为此新类型生成添加和移除处理程序,以便此类的客户端可以对实例的调用列表添加和移除方法。The compiler also generates add and remove handlers for this new type so that clients of this class can add and remove methods from an instance's invocation list. 编译器会强制所添加或移除的方法的签名与声明该方法时使用的签名匹配。The compiler will enforce that the signature of the method being added or removed matches the signature used when declaring the method.

声明委托的实例Declare instances of delegates

定义委托之后,可以创建该类型的实例。After defining the delegate, you can create an instance of that type. 与 C# 中的所有变量一样,不能直接在命名空间中或全局命名空间中声明委托实例。Like all variables in C#, you cannot declare delegate instances directly in a namespace, or in the global namespace.

// inside a class definition:

// Declare an instance of that type:
public Comparison<T> comparator;

变量的类型是 Comparison<T>(前面定义的委托类型)。The type of the variable is Comparison<T>, the delegate type defined earlier. 变量的名称是 comparatorThe name of the variable is comparator.

上面的代码片段在类中声明了一个成员变量。That code snippet above declared a member variable inside a class. 还可以声明作为局部变量或方法参数的委托变量。You can also declare delegate variables that are local variables, or arguments to methods.

调用委托Invoke delegates

可通过调用某个委托来调用处于该委托调用列表中的方法。You invoke the methods that are in the invocation list of a delegate by calling that delegate. Sort() 方法内部,代码会调用比较方法以确定放置对象的顺序:Inside the Sort() method, the code will call the comparison method to determine which order to place objects:

int result = comparator(left, right);

在上面的行中,代码会调用 附加到委托的方法。In the line above, the code invokes the method attached to the delegate. 可将变量视为方法名称,并使用普通方法调用语法调用它。You treat the variable as a method name, and invoke it using normal method call syntax.

该代码行进行了不安全假设:不保证目标已添加到委托。That line of code makes an unsafe assumption: There's no guarantee that a target has been added to the delegate. 如果未附加目标,则上面的行会导致引发 NullReferenceExceptionIf no targets have been attached, the line above would cause a NullReferenceException to be thrown. 用于解决此问题的惯例比简单 null 检查更加复杂,在此系列的后面部分中会进行介绍。The idioms used to address this problem are more complicated than a simple null-check, and are covered later in this series.

分配、添加和删除调用目标Assign, add, and remove invocation targets

这是委托类型的定义方式,以及声明和调用委托实例的方式。That's how a delegate type is defined, and how delegate instances are declared and invoked.

要使用 List.Sort() 方法的开发人员需要定义签名与委托类型定义匹配的方法,并将它分配给排序方法使用的委托。Developers that want to use the List.Sort() method need to define a method whose signature matches the delegate type definition, and assign it to the delegate used by the sort method. 此分配会将方法添加到该委托对象的调用列表。This assignment adds the method to the invocation list of that delegate object.

假设要按长度对字符串列表进行排序。Suppose you wanted to sort a list of strings by their length. 比较函数可能如下所示:Your comparison function might be the following:

private static int CompareLength(string left, string right) =>

方法声明为私有方法。The method is declared as a private method. 这没有什么不对。That's fine. 你可能不希望此方法是公共接口的一部分。You may not want this method to be part of your public interface. 它仍可以在附加到委托时用作比较方法。It can still be used as the comparison method when attached to a delegate. 调用代码会将此方法附加到委托对象的目标列表,并且可以通过该委托访问它。The calling code will have this method attached to the target list of the delegate object, and can access it through that delegate.

通过将该方法传递给 List.Sort() 方法来创建该关系:You create that relationship by passing that method to the List.Sort() method:


请注意,在不带括号的情况下使用方法名称。Notice that the method name is used, without parentheses. 将方法用作参数会告知编译器将方法引用转换为可以用作委托调用目标的引用,并将该方法作为调用目标进行附加。Using the method as an argument tells the compiler to convert the method reference into a reference that can be used as a delegate invocation target, and attach that method as an invocation target.

还可以通过声明“Comparison<string>”类型的变量并进行分配来显式执行操作:You could also have been explicit by declaring a variable of type Comparison<string> and doing an assignment:

Comparison<string> comparer = CompareLength;

在用作委托目标的方法是小型方法的用法中,经常使用 lambda 表达式语法来执行分配:In uses where the method being used as a delegate target is a small method, it's common to use lambda expression syntax to perform the assignment:

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);

后续部分中更详细地介绍了如何对委托目标使用 lambda 表达式。Using lambda expressions for delegate targets is covered more in a later section.

Sort() 示例通常将单个目标方法附加到委托。The Sort() example typically attaches a single target method to the delegate. 但是,委托对象支持将多个目标方法附加到委托对象的调用列表。However, delegate objects do support invocation lists that have multiple target methods attached to a delegate object.

委托和 MulticastDelegate 类Delegate and MulticastDelegate classes

上面介绍的语言支持可提供在使用委托时通常需要的功能和支持。The language support described above provides the features and support you'll typically need to work with delegates. 这些功能采用 .NET Core Framework 中的两个类进行构建:DelegateMulticastDelegateThese features are built on two classes in the .NET Core framework: Delegate and MulticastDelegate.

System.Delegate 类及其单个直接子类 System.MulticastDelegate 可提供框架支持,以便创建委托、将方法注册为委托目标以及调用注册为委托目标的所有方法。The System.Delegate class and its single direct subclass, System.MulticastDelegate, provide the framework support for creating delegates, registering methods as delegate targets, and invoking all methods that are registered as a delegate target.

有趣的是,System.DelegateSystem.MulticastDelegate 类本身不是委托类型。Interestingly, the System.Delegate and System.MulticastDelegate classes are not themselves delegate types. 它们为所有特定委托类型提供基础。They do provide the basis for all specific delegate types. 相同的语言设计过程要求不能声明派生自 DelegateMulticastDelegate 的类。That same language design process mandated that you cannot declare a class that derives from Delegate or MulticastDelegate. C# 语言规则禁止这样做。The C# language rules prohibit it.

相反,C# 编译器会在你使用 C# 语言关键字声明委托类型时,创建派生自 MulticastDelegate 的类的实例。Instead, the C# compiler creates instances of a class derived from MulticastDelegate when you use the C# language keyword to declare delegate types.

此设计起源于 C# 和 .NET 的第一版。This design has its roots in the first release of C# and .NET. 设计团队的一个目标是确保在使用委托时,语言强制实施类型安全。One goal for the design team was to ensure that the language enforced type safety when using delegates. 这意味着确保使用正确类型和数量的参数来调用委托。That meant ensuring that delegates were invoked with the right type and number of arguments. 并且在编译时正确指示任何返回类型。And, that any return type was correctly indicated at compile time. 委托是 1.0 .NET 版本的一部分(在泛型出现之前)。Delegates were part of the 1.0 .NET release, which was before generics.

强制实施此类型安全的最佳方法是让编辑器创建表示所使用的方法签名的具体委托类。The best way to enforce this type safety was for the compiler to create the concrete delegate classes that represented the method signature being used.

即使不能直接创建派生类,也会使用对这些类定义的方法。Even though you cannot create derived classes directly, you will use the methods defined on these classes. 我们来讨论一下在使用委托时会使用的最常见方法。Let's go through the most common methods that you will use when you work with delegates.

要记住的首要且最重要的事实是,使用的每个委托都派生自 MulticastDelegateThe first, most important fact to remember is that every delegate you work with is derived from MulticastDelegate. 多播委托意味着通过委托进行调用时,可以调用多个方法目标。A multicast delegate means that more than one method target can be invoked when invoking through a delegate. 原始设计考虑区分只能附加并调用一个目标方法的委托与可以附加并调用多个目标方法的委托。The original design considered making a distinction between delegates where only one target method could be attached and invoked, and delegates where multiple target methods could be attached and invoked. 该区分被证明在实际中不如最初设想那么有用。That distinction proved to be less useful in practice than originally thought. 已创建了两个不同的类,并且自初始公开发行以来便一直处于框架中。The two different classes were already created, and have been in the framework since its initial public release.

对委托最常使用的方法是 Invoke()BeginInvoke() / EndInvoke()The methods that you will use the most with delegates are Invoke() and BeginInvoke() / EndInvoke(). Invoke() 会调用已附加到特定委托实例的所有方法。Invoke() will invoke all the methods that have been attached to a particular delegate instance. 如上面所见,通常会通过对委托变量使用方法调用语法来调用委托。As you saw above, you typically invoke delegates using the method call syntax on the delegate variable. 如在此系列后面部分中所见,一些模式可直接使用这些方法。As you'll see later in this series, there are patterns that work directly with these methods.

现在你已了解支持委托的语言语法和类,我们来看一下如何使用、创建和调用强类型委托。Now that you've seen the language syntax and the classes that support delegates, let's examine how strongly typed delegates are used, created, and invoked.