经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
重学c#系列——委托和匿名函数[二十五]
来源:cnblogs  作者:敖毛毛  时间:2022/11/28 9:01:01  对本文有异议

前言

简单介绍一下什么是委托。

正文

以前也写过委托,这次算是重新归档,和新的补充吧。

https://www.cnblogs.com/aoximin/p/13940125.html

有些人说委托是函数指针的包装,也有些人说委托是一个方法或多个方法的引用。

这都是没有问题,委托是一个概念,微软官方文档说委托是一种引用类型,表示对具有特定参数列表和返回类型的方法引用。

我觉得太啰嗦了,实际上就是方法的引用。

上面都是委托的概念,但是实现方式每种语言可能都不一样。

比如c++ 和 c 用的是函数指针,而c# 用的是生成包装类(等下IL),当然本质还是函数指针。

那么来看下委托。

  1. internal class Program
  2. {
  3. delegate int TestDelegate(int a);
  4. static void Main(string[] args)
  5. {
  6. TestDelegate a = test;
  7. a(0);
  8. }
  9. public static int test(int a)
  10. {
  11. return 0;
  12. }
  13. }

将test 给了委托a,然后调用的时候直接a()就可以了。

用起来十分简单。

实际上对IL来说其实是没有委托这个概念的,通过反编译来看下原理。

  1. private static void Main(string[] args)
  2. {
  3. TestDelegate a = new TestDelegate(test);
  4. a(0);
  5. }

实际上会生成TestDelegate这样一个类,然后将test 引用添加进去。

来看下il。

再看下TestDelegate是一个什么样的类。

就是把调用的object 和 方法的引用放入包装类中了,然后invoke 可以进行调用。

如果是多个方法的引用呢?

  1. internal class Program
  2. {
  3. delegate int TestDelegate(int a);
  4. static void Main(string[] args)
  5. {
  6. TestDelegate a = test;
  7. a += test;
  8. a(0);
  9. }
  10. public static int test(int a)
  11. {
  12. return 0;
  13. }
  14. }

看下IL:

原理就是又new了一个TestDelegate,然后用Delegate 将两个相连。

Combine 是一个静态方法哈。

本质是调用a的combineImp这个方法。之所以有这个一个静态方法是为了避免出现a为空的情况,如果a为空,直接把b给a啊。

这个是我们写链式结构可以学习的,这样就不用判断声明的时候是否为空。

然后c# 帮我们提取定义了很多委托,以至于我们几乎不用去声明委托。

比如Func 和 Action,Func 有返回值,Action没有。

下面介绍匿名函数,匿名函数有两个要介绍的,他们分别是匿名方法和lambda表达式。

他们原理都一样,都是生成匿名函数,只是写法不一样。

  1. delegate int TestDelegate(int a);
  2. static void Main(string[] args)
  3. {
  4. TestDelegate a = delegate (int a)
  5. {
  6. return 0;
  7. };
  8. }

看下反编译后的内容。

  1. private static void Main(string[] args)
  2. {
  3. TestDelegate a = <>c.<>9__1_0 ?? (<>c.<>9__1_0 = new TestDelegate(<>c.<>9.<Main>b__1_0));
  4. }

那么看下<>c 这个类:

首先看到第一个框,那么作者的意思是想把 <>c做成一个单例。

里面有委托的引用。然后下面这个

b__1_0 就是生成的方法。

其实匿名方法还是编译帮忙生成对应的方法名。

如果用lambda 表达式写的话,那么是这样写的:

这种写法编译出来的代码一模一样。只是不同写法的问题。

值得注意的是匿名函数如果引用了外部的信息,那么会形成闭包。

比如说:

  1. static void Main(string[] args)
  2. {
  3. Student s = new Student();
  4. TestDelegate a = (a) => {
  5. s = null;
  6. return 0;
  7. };
  8. a += (b) => {
  9. return 0;
  10. };
  11. a += (c) => {
  12. return 0;
  13. };
  14. }

首先b和c(第二个和第三个匿名)没有引用外部对象,那么都会生成在<>c这个类中。

第一个有外部引用生成了另外一个类。

然后实例化<>c__DisplayClass1_0后,那么会将s赋值进来。

所以会形成这种闭包,这是值得注意的地方。

下一节委托的发布订阅与事件。

原文链接:https://www.cnblogs.com/aoximin/p/16922423.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号