C# 委托(delegate)

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有相同签名和返回类型的方法相关联。 你可以通过委托实例调用方法。

委托用于将方法作为参数传递给其他方法。 事件处理程序就是通过委托调用的方法。 你可以创建一个自定义方法,当发生特定事件时,某个类就可以调用你的方法。 下面的示例演示了一个委托声明:

public delegate int PerformCalculation(int x, int y);

可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。 该方法可以是静态方法,也可以是实例方法。 此灵活性意味着你可以通过编程方式来更改方法调用,还可以向现有类中插入新代码

备注

在方法重载的上下文中,方法的签名不包括返回值。 但在委托的上下文中,签名包括返回值。 换句话说,方法和委托必须具有相同的返回类型。

将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。 可编写一个比较应用程序中两个对象的方法。 该方法可用在排序算法的委托中。 由于比较代码与库分离,因此排序方法可能更常见。

对于类似的方案,已将函数指针添加到 C# 9,其中你需要对调用约定有更多的控制。 使用添加到委托类型的虚方法调用与委托关联的代码。 使用函数指针,可以指定不同的约定。

委托具有以下属性:

  • 委托类似于 C++ 函数指针,但委托完全面向对象,不像 C++ 指针会记住函数,委托会同时封装对象实例和方法。
  • 委托允许将方法作为参数进行传递。
  • 委托可用于定义回调方法。
  • 委托可以链接在一起;例如,可以对一个事件调用多个方法。
  • 方法不必与委托类型完全匹配。 有关详细信息,请参阅使用委托中的变体。
  • 使用 Lambda 表达式可以更简练地编写内联代码块。 Lambda 表达式(在某些上下文中)可编译为委托类型。 若要详细了解 lambda 表达式,请参阅 lambda 表达式。

委托是安全封装方法的类型,类似于 C 和 C++ 中的函数指针。 与 C 函数指针不同的是,委托是面向对象的、类型安全的和可靠的。 委托的类型由委托的名称确定。 以下示例声明名为 Del 的委托,该委托可以封装采用字符串作为参数并返回 void 的方法:

public delegate void Del(string message);

委托对象通常可采用两种方式进行构造,一种是提供委托将封装的方法的名称,另一种是使用 lambda 表达式。 对委托进行实例化后,委托会将对其进行的方法调用传递到该方法。 调用方传递到委托的参数将传递到该方法,并且委托会将方法的返回值(如果有)返回到调用方。 这被称为调用委托。 实例化的委托可以按封装的方法本身进行调用。 例如:

// Create a method for a delegate.public static void DelegateMethod(string message){ Console.WriteLine(message);}// Instantiate the delegate.Del handler = DelegateMethod;// Call the delegate.handler(“Hello World”);

委托类型派生自 .NET 中的 Delegate 类。 委托类型是密封的,它们不能派生自 Delegate,也不能从其派生出自定义类。 由于实例化委托是一个对象,因此可以将其作为参数传递或分配给属性。 这允许方法接受委托作为参数并在稍后调用委托。 这被称为异步回调,是在长进程完成时通知调用方的常用方法。 当以这种方式使用委托时,使用委托的代码不需要知道要使用的实现方法。 功能类似于封装接口提供的功能。

回调的另一个常见用途是定义自定义比较方法并将该委托传递到短方法。 它允许调用方的代码成为排序算法的一部分。 以下示例方法使用 Del 类型作为参数:

public static void MethodWithCallback(int param1, int param2, Del callback){ callback(“The number is: ” + (param1 + param2).ToString());}

然后,你可以将上面创建的委托传递到该方法:

MethodWithCallback(1, 2, handler);

并将以下输出接收到控制台:

The number is: 3

以抽象方式使用委托时,MethodWithCallback 不需要直接调用控制台,记住,其不必设计为具有控制台。 MethodWithCallback 的作用是简单准备字符串并将字符串传递到其他方法。 由于委托的方法可以使用任意数量的参数,此功能特别强大。

当委托构造为封装实例方法时,委托将同时引用实例和方法。 委托不知道除其所封装方法以外的实例类型,因此委托可以引用任何类型的对象,只要该对象上有与委托签名匹配的方法。 当委托构造为封装静态方法时,委托仅引用方法。 请考虑以下声明:

public class MethodClass{ public void Method1(string message) { } public void Method2(string message) { }}

加上之前显示的静态 DelegateMethod,我们现在已有三个 Del 实例可以封装的方法。

调用时,委托可以调用多个方法。 这被称为多播。 若要向委托的方法列表(调用列表)添加其他方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。 例如:

var obj = new MethodClass();Del d1 = obj.Method1;Del d2 = obj.Method2;Del d3 = DelegateMethod;//Both types of assignment are valid.Del allMethodsDelegate = d1 + d2;allMethodsDelegate += d3;

此时,allMethodsDelegate 的调用列表中包含三个方法,分别为 Method1、Method2 和 DelegateMethod。 原有的三个委托(d1、d2 和 d3)保持不变。 调用 allMethodsDelegate 时,将按顺序调用所有三个方法。 如果委托使用引用参数,引用将按相反的顺序传递到所有这三个方法,并且一种方法进行的任何更改都将在另一种方法上见到。 当方法引发未在方法内捕获到的异常时,该异常将传递到委托的调用方,并且不会调用调用列表中的后续方法。 如果委托具有返回值和/或输出参数,它将返回上次调用方法的返回值和参数。 若要删除调用列表中的方法,请使用减法运算符或减法赋值运算符(- 或 -=)。 例如:

//remove Method1allMethodsDelegate -= d1;// copy AllMethodsDelegate while removing d2Del oneMethodDelegate = allMethodsDelegate – d2;

由于委托类型派生自 System.Delegate,因此可以在委托上调用该类定义的方法和属性。 例如,若要查询委托调用列表中方法的数量,你可以编写:

int invocationCount = d1.GetInvocationList().GetLength(0);

调用列表中具有多个方法的委托派生自 MulticastDelegate,该类属于 System.Delegate 的子类。 由于这两个类都支持 GetInvocationList,因此在其他情况下,上述代码也将产生作用。

多播委托广泛用于事件处理中。 事件源对象将事件通知发送到已注册接收该事件的接收方对象。 若要注册一个事件,接收方需要创建用于处理该事件的方法,然后为该方法创建委托并将委托传递到事件源。 事件发生时,源调用委托。 然后,委托将对接收方调用事件处理方法,从而提供事件数据。 给定事件的委托类型由事件源确定。 有关详细信息,请参阅事件。

在编译时比较分配的两个不同类型的委托将导致编译错误。 如果委托实例是静态的 System.Delegate 类型,则允许比较,但在运行时将返回 false。 例如:

delegate void Delegate1();delegate void Delegate2();static void method(Delegate1 d, Delegate2 e, System.Delegate f){ // Compile-time error. //Console.WriteLine(d == e); // OK at compile-time. False if the run-time type of f // is not the same as that of d. Console.WriteLine(d == f);}

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月24日
下一篇 2022年6月24日

相关推荐

  • 李小璐结婚纪念日微博发文:想你了,贾乃亮却直接回怼,回应绝了

    李小璐和贾乃亮,这对夫妻相信大家都是不陌生的,曾经他们在人们的眼中是金童玉女,天造地设的一对佳人,非常让人羡慕,加上他们有了可爱的女儿甜馨以后更是给了大家太多的欢乐和对美好生活的向…

    2022年7月14日
  • 「技术干货」一文搞懂Linux内核调试方法(二)

    上篇回顾:一文Linux内核调试方法(一) KGDB kgdb提供了一种使用 gdb调试 Linux 内核的机制。使用KGDB可以象调试普通的应用程序那样,在内核中进行设置断点、检…

    2022年6月18日
  • “夜光剧本”过去6年,杨幂和王鸥差距明显,其实一切都有征兆

    2016年11月9日,北京。 刚参加完时尚Cosmo美丽盛典的杨幂,一脸疲惫地现身首都机场。 在不久前,她刚刚得知丈夫刘恺威“疑似出轨”女星王鸥的绯闻。 她虽然不知道王鸥怎样,但却…

    2022年5月14日
  • from…import、import……as与from…import* 的含义与区别

    一、import…as import:导入一个模块;注:相当于导入的是一个文件夹,是个相对路径 import A as B:给予工具库 A 一个简单的别称 B ,可以帮助记忆。例:…

    2022年6月19日
  • 科学家警告:「太空垃圾」从天而降可能威胁生命

    太空碎片袭击地球。(示意图:Public Domain) 随着人类越来越有规律地进入轨道,科学家警告说,探索产生的碎片可能威胁地球上的生命,甚至阻止我们继续探索太空。 本月早些时候…

    2022年8月24日
  • Java 服务 Docker 容器化最佳实践

    一、概述 当我们在容器中运行 Java 应用程序时,可能希望对其进行调整参数以充分利用资源。 在本教程中,我们将了解如何在运行 Java 进程的容器中设置 JVM 参数。本文将重点…

    2022年8月7日
  • 开源鸿蒙OpenHarmony图标Logo正式发布

    IT之家 7 月 30 日消息,7 月 27 日,2022 开放原子全球开源峰会 OpenAtom OpenHarmony 分论坛在北京成功举办。本次论坛以“万物互联,使能千行百业…

    2022年8月1日
  • 科学护肤步骤,养成牛奶肌

    近几年,我国护肤品行业急速发展,护肤成为男女老少皆宜的活动。可有些人只是在乱护肤,在自我沉浸式护肤,这样护肤不仅达到不养护肌肤的效果,还浪费了时间和金钱。今天给大家分享正确的护肤方…

    2022年8月17日
  • 看,这才是去黑头的正确姿势

    如果你被黑头折磨的不够狠! 如果你在去黑头的路上走的弯路不够多! 如果你为了去黑头花得钱不够冤! 先不要看我的文章! 建议你试遍其他的方法再来,相信我咱们一定会相见恨晚,抱头痛哭!…

    2022年8月25日
  • 微商加粉是6种方式(微商加精准粉的方法有哪些)

    之前很多人推崇的QQ群营销放到现在其实已经都不太适用了,你会发现每个群里发广告的会比客户占比多的太多了,所以效果也越来越差。而相比之下,微信群营销虽然目前开发还不是特别明确,但是其…

    2022年11月7日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息