【C#学习笔记】匿名函数和lambda表达式

在这里插入图片描述

文章目录

  • 匿名函数
    • 匿名函数的定义
    • 匿名函数作为参数传递
    • 匿名函数的缺点
  • lambda表达式
    • 什么是lambda表达式
    • 闭包


匿名函数

为什么我们要使用匿名函数?匿名函数存在的意义是为了简化一些函数的定义,特别是那些定义了之后只会被调用一次的函数,与其大费周折拖动文件然后定义在类中的某个位置,匿名函数将更加方便简洁。

匿名函数的定义

以下两种无参数匿名函数:

Action A = delegate { Debug.Log("Hello"); };
A += delegate () 
{Debug.Log("Hello"); 
};

匿名函数声明需要配合委托使用,并且声明时需要在函数头加上delegate,匿名函数的无参构造可以省略括号。
匿名函数定义不允许使用泛型(很好理解,泛型是为了函数调用时能够灵活地接受不同类型的参数,但是使用匿名函数意味着它只会被调用一次,与其使用泛型不如让我们直接指定)

Action<int, string> A = delegate (int i, string s)
{int q = Int32.Parse(s) + i;
};

定义带参数的匿名函数只需像正常函数定义即可。如果需要返回值只需使用Func

Func<int, string,int> A = delegate (int i, string s)
{int q = Int32.Parse(s) + i;return q;
};

匿名函数作为参数传递

现有如下定义:

    class Test{public Action action;public void Dosomething(int a ,Action fun){fun();}public Action MyFun(){return delegate () { Debug.Log("返回委托类型的匿名函数"); };}}

在上述类中,我们定义了两个方法,Dosomething需要传入一个Action委托funMyfun则返回一个Action类型的返回值(定义的委托是可以作为返回值类型的),在其中我们将一个匿名函数作为委托类型的返回值。

    void Start(){Test t = new Test();t.Dosomething(1, delegate { Debug.Log("匿名函数作为参数传入"); });Action A = delegate { Debug.Log("委托装载匿名函数并作为参数传入)"); };t.Dosomething(1, A);}

匿名函数可以作为委托类型的参数传入。也可以返回时作为委托类型(猜测应当是匿名函数作为参数传递时自动被封装为了对应委托):

    void Start(){Test t = new Test();t.action = t.MyFun();t.action(); // 输出:返回委托类型的匿名函数t.MyFun()(); // 只有委托返回值类型才能这么使用,调用函数后再加个括号直接调用委托}

委托类型返回值的函数在调用后再加上括号可以直接调用该委托。


匿名函数的缺点

使用匿名函数的最大优点就是方便,但是匿名函数最大的缺点就是匿名,如下所示:

Action A = delegate { Debug.Log("你好"); };
A -= delegate { Debug.Log("你好"); }; // 无效
A();// 依旧会输出你好,因为匿名函数无法删除
A = null; // 只有清空A才能从中删除匿名函数

想要删除委托中的匿名函数,即使我们在A中减去相同定义的匿名函数也无济于事,因为此函数非彼函数。匿名函数没有函数名,也就无法被减去,即使我们定义了相同的匿名函数,他们的地址本质上也不是同一个函数。

所以如果一个委托只存一个函数,那就可以使用匿名函数,但是如果一个委托存在多个函数,那么当你想要去除这个匿名函数的时候,就只能清空这个委托。所以在设计使用匿名函数的时候,要么用一个委托只存储匿名函数,要么作为参数传入委托类型的返回值的函数进行调用(就像上述的t.MyFun()();)。


lambda表达式

什么是lambda表达式

lambda表达式就是匿名函数的一种创建方式,使用lambda表达式可以省略匿名函数的delegate定义:

Action A = () => { Debug.Log("你好"); };

lambda表达式省略了delegate关键字,并使用 lambda 声明运算符=>来声明。 同理,有参和有返回的lambda表达式也和匿名函数的定义相同。

        Action<int, string> B = (int i, string s) =>{int q = Int32.Parse(s) + i;};Func<int, string,int> C = (int i, string s) =>{int q = Int32.Parse(s) + i;return q;};// 委托已经指定类型,可以省略参数类型Func<int, string,int> C = (i, s) =>{int q = Int32.Parse(s) + i;return q;};

当lambda表达式只执行一个语句的时候,连花括号都不需要了, 使用空括号指定0个输入参数:

Action line = () => Console.WriteLine();

不带返回值类型的lambda表达式的简洁用法,单个参数可以省略小括号:

Action<int> square = x => Console.WriteLine("x");

甚至lambda表达式可以省略返回值定义,只需定义委托类型即可:

Func<int, int> square = x => x * x; // return x*x,在带返回值类型的委托中省略return
Func<int, int, bool> testForEquality = (x, y) => x == y; //多个入参还是需要括号和逗号区分的

在多个入参的情况下,lambda用弃元来表示哪些参数不被使用
(多嘴一下,在委托定义了入参数量,然后lambda表达式需要使用到的入参比定义的入参数量少的情况下当然可以使用弃元。但是作为添加到委托内的匿名函数,如果委托需要装载多个函数的话,实际上不太适合使用匿名函数):

Func<int, int, int> constant = (_, _) => 42;

闭包

关于闭包的具体说明已经在此文【Lua学习笔记】Lua进阶——函数和闭包中解释过了,此处便不再赘述。

简单来说,闭包就是内部函数引用了外部函数的变量,导致本应该在栈中释放的外部函数的生命周期遭到改变(延长)。这个(这些)变量被我们称为upvalues,它们原本的作用域是外部函数,但是随着内部函数的使用,它们的作用域又包括了内部函数。

就如同下面的例子:

public event Action action;
public void Test()
{int value = 100;action = () => { Debug.Log(value); };//使用了value,但value作用域在函数Test而非匿名函数内
}

上述例子中我们在匿名函数内部使用了外部函数Test的变量value,结果是可以正常执行的。照理说局部变量valueTest中没被使用就会释放,但是匿名函数的使用延长了它的生命周期,这就是闭包。

再看下列的例子:

public void Test()
{for (int i = 0; i < 10; i++){action += () => { Debug.Log(i); };}
}
action();

当我们执行上述语句的时候输出答案是什么?可能你以为是0123456789,但真正的答案是输出10次10,原因其实也很简单,在委托中存储了十个匿名函数,每个的指令是输出i,但是不要忘了i是upvalue,它不是赋值到内部这个匿名函数的局部变量,而是匿名函数直接调用在外部函数的i的值,当循环结束时i=10,本应当释放的i的生命周期得到了延长,当我们执行委托的时候,则匿名函数才会调用这个i,而此时i=10,因此输出了10次10。

public void Test()
{for (int i = 0; i < 10; i++){int index = i;action += () => { Debug.Log(index); };}
}
action();

然而使用int index = i则可以实现上述功能,原因在于它是值类型的变量,我们每次在for循环中int一个index,实际上都是声明了一个新的int变量。所以每个匿名函数调用的index实际上是10个不同的index。

如果害怕lambda表达式不小心捕获了外部函数的upvalue,则可以使用static关键字进行限制:

Func<double, double> square = static x => x * x;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/101809.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ribbon:自定义负载均衡

自定义负载均衡算法 package com.kuang.myconfig;import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server;import java.util.…

STM32f103c6t6/STM32f103c8t6寄存器开发

目录 资料 寻址区 2区 TIMx RTC WWDG IWDG SPI I2S USART I2C USB全速设备寄存器 bxCAN BKP PWR DAC ADC ​编辑 EXTI ​编辑 GPIO AFIO SDIO DMA CRC RCC FSMC USB_OTG ETH&#xff08;以太网&#xff09; 7区 配置流程 外部中断 硬件中断 例子 点灯 …

typora的样式的修改

typora首先是一个浏览器&#xff0c; 当我们在typora的设置里面勾选开启调试模式之后&#xff0c; 我们在typora里面右键就会有“检查元素” 这个选项 首先右键 ----》检查元素 将普通字体变颜色 关于Typora修改样式 破解版的typora样式太单调&#xff1f;想让笔记可读性更高…

Numpy学习笔记

科学计算库&#xff08;Numpy&#xff09; 通常数据都能转换成矩阵&#xff0c;行就是每一条样本数据&#xff0c;列就是每个字段的特征&#xff0c;Numpy在矩阵运算上非常高效&#xff0c;可以快速处理数据并进行数据计算。 Numpy基本操作 先导入 import numpy as nparray…

计算机网络第3章(数据链路层)

计算机网络第3章&#xff08;数据链路层&#xff09; 3.1 数据链路层概述3.1.1 概述3.1.2 数据链路层使用的信道3.1.3 三个重要问题 3.2 封装成帧3.2.1 介绍3.2.2 透明传输3.2.3 总结 3.3 差错检测3.3.1 介绍3.3.2 奇偶校验3.3.3 循环冗余校验CRC(Cyclic Redundancy Check)3.3.…

Python Pandas 处理Excel数据 制图

目录 1、饼状图 2、条形统计图 1、饼状图 import pandas as pd import matplotlib.pyplot as plt import numpy as np #from matplotlib.ticker import MaxNLocator # 解决中文乱码 plt.rcParams[font.sans-serif][SimHei] plt.rcParams[font.sans-serif]Microsoft YaHei …

网络聊天室

一、项目要求 利用UDP协议&#xff0c;实现一套聊天室软件。服务器端记录客户端的地址&#xff0c;客户端发送消息后&#xff0c;服务器群发给各个客户端软件。 问题思考 客户端会不会知道其它客户端地址&#xff1f; UDP客户端不会直接互连&#xff0c;所以不会获知其它客…

计算机网络-物理层(三)编码与调制

计算机网络-物理层&#xff08;三&#xff09;编码与调制 在计算机网络中&#xff0c;计算机需要处理和传输用户的文字、图片、音频和视频&#xff0c;它们可以统称为消息 数据是运输信息的实体&#xff0c;计算机只能处理二进制数据&#xff0c;也就是比特0和比特1。计算机中…

【Java 动态数据统计图】动态数据统计思路案例(动态,排序,数组)二(113)

需求&#xff1a; 有一个List<Map<String.Object>>,存储了区域的数据&#xff0c; 数据是根据用户查询条件进行显示的&#xff1b;所以查询的数据是动态的&#xff1b;按区域维度统计每个区域出现的次数&#xff0c;并且按照次数的大小排序&#xff08;升序&#…

科技资讯|荷兰电动自行车丢失将被拒保,苹果Find My可以减少丢失

荷兰最大的自行车协会荷兰皇家旅游俱乐部宣布&#xff0c;将不再为胖胎电动自行车提供保险&#xff0c;因为这种自行车的被盗风险极高。 随着电动自行车的销量飙升&#xff0c;胖胎也变得更受欢迎。但问题是&#xff0c;胖胎电动自行车也成为了自行车盗窃者的首选目标。ANWB …

Android 源码下载(详细版)

经典好文推荐,通过阅读本文,您将收获以下知识点: 一、下载AOSP前的准备 二、国内网络下 clone 清华大学开源软件镜像 三、编写Python脚本,开始下载android-10.0.0_r40 源码 四、源码下载工具包 五、参考文献 一、下载AOSP前的准备 想在国内网络下载AOSP源码,需要电脑配置如…

jvm-虚拟机栈

1.栈的存储单位 栈是运行时单位&#xff0c;而堆是存储的单位 栈解决程序的运行问题&#xff0c;即程序如何执行&#xff0c;或者说如何处理数据。堆解决的是数据存储问题&#xff0c;即数据怎么放&#xff0c;放在哪儿 java虚拟机栈 早期也叫java栈&#xff0c;每个线程在创建…

JMETER基本原理

Jmeter基本原理是建立一个线程池&#xff0c;多线程运行取样器产生大量负载&#xff0c;在运行过程中通过断言来验证结果的正确性&#xff0c;可以通过监听来记录测试结果&#xff1b; JMETER是运行在JVM虚拟机上的&#xff0c;每个进程的开销比loadrunner的进程开销大&#x…

组件库的使用和自定义组件

目录 一、组件库介绍 1、什么是组件 2、组件库介绍 3、arco.design 二、组件库的使用 1、快速上手 2、主题定制 3、暗黑模式 4、语言国际化 5、业务常见问题 三、自定义组件 2、组件开发规范 3、示例实践guide-tip 4、业务组件快速托管 一、组件库介绍 1、什么是…

基于Spring Boot的社区诊所就医管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的社区诊所就医管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java …

Docker安装并配置cAdvisor

Linux下安装Docker请参考&#xff1a;Linux安装Docker 简介 cAdvisor 是 Google 开源的一款用于展示和分析容器运行状态的可视化工具。通过在主机上运行 CAdvisor 用户可以轻松的获取到当前主机上容器的运行统计信息&#xff0c;并以图表的形式向用户展示。 cAdvisor 可以对…

疲劳驾驶检测和识别4:C++实现疲劳驾驶检测和识别(含源码,可实时检测)

疲劳驾驶检测和识别4&#xff1a;C实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 目录 疲劳驾驶检测和识别4&#xff1a;C实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 1.疲劳驾驶检测和识别方法 2.人脸检测方法 3.疲劳驾驶识别模型(Python) &#xf…

[oneAPI] 使用字符级 RNN 生成名称

[oneAPI] 使用字符级 RNN 生成名称 oneAPI特殊写法使用字符级 RNN 生成名称Intel Optimization for PyTorch数据下载加载数据并对数据进行处理创建网络训练过程准备训练训练网络 结果 参考资料 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517…

Temu闯关日韩受挫?跨境电商卖家如何打磨好营销链路

海外版拼多多 Temu 先后在日本和韩国上线&#xff0c;然而效果不似预期&#xff0c;日韩市场对这套“低价补贴”策略并不买账。作为一个尚未被日韩消费者熟悉的网站&#xff0c;其价格之便宜无法让消费者信任。除此之外更大的问题是&#xff0c;在日本卷不过线下零售与百元店&a…

生信学院|08月25日《SOLIDWORKS PDM帮助企业对设计数据版本的管理应用》

课程主题&#xff1a;SOLIDWORKS PDM帮助企业对设计数据版本的管理应用 课程时间&#xff1a;2023年08月25日 14:00-14:30 主讲人&#xff1a;车立洋 生信科技 PDM专家 1、图纸&文档的版本管理对于企业的重要性 2、SolidWorks PDM对图纸&文档版本的管理 3、SolidW…