Logo
Section 1 / 7

委托

创建可以引用并调用其他方法的类型。

导入

委托

创建可以引用并调用其他方法的类型。

## 创建并调用委托

委托 是指向方法的对象。换句话说,它们保存了方法定义的引用。通过调用委托,我们可以调用该委托所指向的所有方法。

委托可以引用方法
委托可以引用方法

委托使用 delegate 关键字声明。之后,我们只需写出带有返回类型的函数签名:

1delegate void MessagePrinter();

此委托可以指向返回 void 且不接受参数的方法。让我们更详细地分析语法以进一步澄清:

...
委托声明的分解

我们可以在类内部或直接在命名空间中创建委托,因为委托本质上是隐式继承自 Delegate 或 MulticastDelegate 类的类。

注意: 我们不能创建一个类并让它继承自 Delegate 或 MulticastDelegate。只有编译器和平台可以显式继承这些类。我们必须使用 delegate 关键字。

声明委托后,我们可以创建其实例,附加方法指针,并调用委托:

...
委托使用示意图

使用静态方法的委托

1using System; 2 3namespace Delegates 4{ 5 // 返回 void 且不接受任何参数 6 delegate void MessagePrinter(); 7 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 // 声明委托类型的变量 13 // 并将其指向某个方法 14 MessagePrinter printer = new MessagePrinter(PrintHelloWorld); 15 16 // 不使用构造函数,也可以直接通过方法指针初始化 17 MessagePrinter printer2 = PrintHelloWorld; 18 19 // 将另一个方法添加到委托的调用列表中 20 printer += PrintGoodBye; 21 printer2 += PrintGoodBye; 22 23 // 调用委托 24 printer(); 25 26 // 另一种调用委托的方式 27 printer2.Invoke(); 28 } 29 30 // 返回 void 且不接受任何参数 31 static void PrintHelloWorld() 32 { 33 Console.WriteLine("Hello World!"); 34 } 35 36 // 返回 void 且不接受任何参数 37 static void PrintGoodBye() 38 { 39 Console.WriteLine("Good bye!"); 40 } 41 } 42}

方法引用与调用方法之间存在区别。当我们输入方法名称(如 PrintHelloWorld)时,我们使用的是对方法的引用。当我们加上括号(如 PrintHelloWorld())时,我们是在调用该方法。要将方法附加到委托上,必须传递方法的引用而不执行它。

在上面的例子中,我们使用了静态方法与委托。静态方法不使用任何实例变量来存储对象状态,因此应更容易理解。当然,也可以将委托用于实例方法。

使用实例方法的委托

1using System; 2 3namespace Delegates 4{ 5 delegate void MessagePrinter(); 6 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 // 创建 Dog 类的实例 12 Dog dog = new Dog("Roxie"); 13 Dog dog2 = new Dog("Buzz"); 14 15 // 附加方法 16 MessagePrinter printer = dog.Bark; 17 printer += dog2.Bark; 18 19 // 调用委托 20 printer(); 21 } 22 } 23}

请注意,输出结果会因实例的状态不同而不同。当我们将实例方法添加到委托的调用列表时,委托会保存与该方法关联的实例引用。

例如,当我们把 Bark() 方法添加到委托时,dog 变量所指向的对象的引用也被传递给了委托。

合并委托

多个相同类型的委托实例可以合并。结果是,所有委托中的方法都会被执行:

1using System; 2 3namespace Delegates 4{ 5 delegate void MessagePrinter(string message); 6 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 // 创建相同类型的委托 12 MessagePrinter printer1 = SurroundWithHyphens; 13 MessagePrinter printer2 = SurroundWithStars; 14 MessagePrinter printer3 = SurroundWithStars; 15 16 // 合并委托 17 MessagePrinter printer4 = printer1 + printer2 + printer3; 18 19 // 调用委托 20 printer4("Hello World!"); 21 } 22 23 static void SurroundWithStars(string message) 24 { 25 Console.WriteLine($"***{message}***"); 26 } 27 28 static void SurroundWithHyphens(string message) 29 { 30 Console.WriteLine($"---{message}---"); 31 } 32 } 33}

如果将同一个方法多次添加到委托的调用列表中,该方法就会被执行多次。在此上下文中,委托不会进行任何过滤。

委托作为方法参数

委托可以作为方法参数传递:

1using System; 2 3namespace Delegates 4{ 5 delegate void MessagePrinter(string message); 6 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 MessagePrinter printer = SurroundWithHyphens; 12 printer += SurroundWithStars; 13 14 MethodInvoker(printer, "some message"); 15 } 16 17 // 该方法接受类型为 MessagePrinter 的委托 18 static void MethodInvoker(MessagePrinter printerDelegate, string message) 19 { 20 // 通常使用 ? 是因为委托可能为空(null) 21 printerDelegate?.Invoke(message); 22 } 23 24 static void SurroundWithStars(string message) 25 { 26 Console.WriteLine($"***{message}***"); 27 } 28 29 static void SurroundWithHyphens(string message) 30 { 31 Console.WriteLine($"---{message}---"); 32 } 33 } 34}

泛型委托

与方法类似,委托也可以是泛型的。

1using System; 2 3namespace Delegates 4{ 5 // 泛型委托 6 delegate T2 MathOperator<T1, T2>(T1 operand1, T1 operand2); 7 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 // 实例化泛型委托 13 MathOperator<int, long> operator1 = Adder; 14 MathOperator<int, double> operator2 = Divider; 15 16 long sum = operator1(5, 6); 17 double divisionResult = operator2(7, 2); 18 19 Console.WriteLine(sum); 20 Console.WriteLine(divisionResult); 21 } 22 23 static long Adder(int firstNumber, int secondNumber) 24 { 25 return firstNumber + secondNumber; 26 } 27 28 static double Divider(int firstNumber, int secondNumber) 29 { 30 return (double)firstNumber / secondNumber; 31 } 32 } 33}

C# 编程基础

匿名方法