• 重学c#系列——委托和匿名函数[二十五]


    前言

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

    正文

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

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

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

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

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

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

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

    那么来看下委托。

    internal class Program
    {
    	delegate int TestDelegate(int a);
    
    	static void Main(string[] args)
    	{
    		TestDelegate a = test;
    		a(0);
    	}
    
    	public static int test(int a)
    	{
    		return 0;
    	}
    }
    

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

    用起来十分简单。

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

    private static void Main(string[] args)
    {
    	TestDelegate a = new TestDelegate(test);
    	a(0);
    }
    

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

    来看下il。

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

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

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

    internal class Program
    {
    	delegate int TestDelegate(int a);
    
    	static void Main(string[] args)
    	{
    		TestDelegate a = test;
    		a += test;
    		a(0);
    	}
    
    	public static int test(int a)
    	{
    		return 0;
    	}
    }
    

    看下IL:

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

    Combine 是一个静态方法哈。

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

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

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

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

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

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

    delegate int TestDelegate(int a);
    
    static void Main(string[] args)
    {
    	TestDelegate a = delegate (int a)
       {
    	   return 0;
       };
    }
    

    看下反编译后的内容。

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

    那么看下<>c 这个类:

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

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

    b__1_0 就是生成的方法。

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

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

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

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

    比如说:

    static void Main(string[] args)
    {
    	Student s = new Student();
    	TestDelegate a = (a) => {
    		s = null;
    
    		return 0;
    	};
    
    	a += (b) => {
    		return 0;
    	};
    
    	a += (c) => {
    		return 0;
    	};
    }
    

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

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

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

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

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

  • 相关阅读:
    2022 OceanBase 年度发布会:发布四大策略,迈入4.0时代
    glog从下载到使用实录
    Python爬虫抓取经过JS加密的API数据的实现步骤
    MariaDB的安装与配置
    libevent学习——例子.md
    《Vue.js 3移动应用开发实战》简介
    【2023年11月第四版教材】第24章《法律法规与标准规范》(合集篇)
    MqttAndroidClient 关闭日志 mq报错日志路径
    商铺分类介绍
    Spring事务执行原理源码
  • 原文地址:https://www.cnblogs.com/aoximin/p/16922423.html