【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委托fun,Myfun则返回一个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,结果是可以正常执行的。照理说局部变量value在Test中没被使用就会释放,但是匿名函数的使用延长了它的生命周期,这就是闭包。
再看下列的例子:
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;
相关文章:
【C#学习笔记】匿名函数和lambda表达式
文章目录 匿名函数匿名函数的定义匿名函数作为参数传递匿名函数的缺点 lambda表达式什么是lambda表达式闭包 匿名函数 为什么我们要使用匿名函数?匿名函数存在的意义是为了简化一些函数的定义,特别是那些定义了之后只会被调用一次的函数,与其…...
百度Apollo:引领自动驾驶技术创新的先锋
文章目录 前言一、内容总结 前言 大家好,我是萝卜头不吃萝卜头,今天和大家分享一下我学习百度Apollo自动驾驶的心得。 在七月份的时候,我收到了Apollo开发者社区的邀请,进行学习Apollo自动驾驶汽车的2023星火培训训练,…...
Redis 重写 AOF 日志期间,主进程可以正常处理命令吗?
重写 AOF 日志的过程是怎样的? Redis 的重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的,这么做有以下两个好处。 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程子进程带有主进程的数据副本。这里…...
java实现生成RSA公私钥、SHA256withRSA加密以及验证工具类
前言: RSA属于非对称加密。所谓非对称加密,需要两个密钥:公钥 (publickey) 和私钥 (privatekey)。公钥和私钥是一对,如果用公钥对数据加密,那么只能用对应的私钥解密。如果用私钥对数据加密,只能用对应的公…...
lab7 thread
文章目录 Uthread: switching between threadstaskhints思路上下文的恢复和保存thread_createthread_schedule Using threads思路 Barrier Uthread: switching between threads 在这个练习中,你将为一个用户级别线程系统设计上下文切换机制,并实现它。 …...
接口自动化测试:mock server之Moco工具
什么是mock server mock:英文可以翻译为模仿的,mock server是我们用来解除依赖(耦合),假装实现的技术,比如说,前端需要使用某些api进行调试,但是服务端并没有开发完成这些api&#…...
用python从零开始做一个最简单的小说爬虫带GUI界面(2/3)
目录 前一章博客 前言 主函数的代码实现 逐行代码解析 获取链接 获取标题 获取网页源代码 获取各个文章的链接 函数的代码 导入库文件 获取文章的标题 获取文章的源代码 提取文章目录的各个文章的链接 总代码 下一章内容 前一章博客 用python从零开始做一个最简单…...
CEF 缓存处理:清理缓存、禁用缓存、忽略缓存
目录 一、CEF缓存处理 1、指定缓存路径 2、清理缓存 3、禁用缓存 1)、原理分析...
Android 系统桌面 App —— Launcher 开发(1)
Android 系统桌面 App —— Launcher 开发(1) Launcher简介 Launcher就是Android系统的桌面,俗称“HomeScreen”也就是我们开机后看到的第一个App。launcher其实就是一个app,它的作用是显示和管理手机上其他App。目前市场上有很…...
一个程序员的工作日记--每天就干两件事,一年后让别人刮目相看
文章目录 成功源于专注一、早上布局二、晚上复盘三、技术细节四、专注与成功五、专注的重要性六、忙碌和赚钱七、结论以嵌入式开发为例:一、早上布局二、晚上复盘三、技术细节四、专注与成功五、忙碌和赚钱六、结论在嵌入式软件开发中,我们需要按照以下步…...
Linux虚拟机安装(Ubuntu 20)
最近这段时间使用VMWare安装了一下Ubuntu版本的Linux虚拟机,在这里记录一下安装时参考的文章以及需要注意的细节 参考链接: VMware虚拟机下安装Ubuntu20.04(保姆级教程) 一、安装VMWare 下载链接:VMware Workstatio…...
1.6 服务器处理客户端请求
客户端进程向服务器进程发送一段文本(MySQL语句),服务器进程处理后再向客户端进程发送一段文本(处理结果)。 从图中我们可以看出,服务器程序处理来自客户端的查询请求大致需要经过三个部分,分别…...
火山引擎发布自研视频编解码芯片 压缩效率提升30%
8月22日,火山引擎视频云宣布其自研的视频编解码芯片已成功出片。经验证,该芯片的视频压缩效率相比行业主流硬件编码器可提升30%以上,未来将服务于抖音、西瓜视频等视频业务,并将通过火山引擎视频云开放给企业客户。 火山引擎总裁…...
从头开始:将新项目上传至Git仓库的简易指南
无论您是一个经验丰富的开发者还是一个刚刚起步的新手,使用Git来管理您的项目是一个明智的选择。Git是一个强大的版本控制系统,它可以帮助您跟踪项目的变化、合并代码以及与团队成员协作。在本文中,我们将为您提供一步步的指南,教…...
数据库的增量备份与差异备份
在当今数字时代,数据已经成为公司的主要资产。为了维护这些珍贵的数据,公司通常会采取各种数据保护措施,其中增量备份是一种很有效的方法。本文将详细介绍什么是数据库的增量备份,以及如何帮助企业更有效地维护数据。 我们需要…...
视频云存储/安防监控视频智能分析网关V3:占道经营功能详解
违规占道经营者经常会在人流量大、车辆集中的道路两旁摆摊,导致公路交通堵塞,给居民出行的造成不便,而且违规占路密集的地方都是交通事故频频发生的区域。 TSINGSEE青犀视频云存储/安防监控视频/AI智能分析网关V3运用视频AI智能分析技术&…...
卡尔曼滤波学习笔记
Kalman Filter Ⅰ、直观理解1、描述2、例子 Ⅱ、适用范围1、线性系统2、噪声服从高斯分布 Ⅲ、相关公式1、原始公式2、预测公式3、更新公式4、初值赋予5、总结 Ⅳ、应用例子Ⅴ、代码实现Ⅵ、公式理解1、协方差矩阵的理解1.1 协方差1.2 协方差矩阵1.3、相关数学公式 2、状态方程…...
NLP预训练模型超大规模探索
总共从四方面来进行比较。 第一个方面,高层次方法(自监督的预训练方法)对比,总共三种方式。 语言模型式,就是 GPT-2 那种方式,从左到右预测;BERT-style 式,就是像 BERT 一样将一部…...
OpenCV实战系列总目录(更新中)
1、openCV实战-系列教程1:基本操作(环境配置/图像读取打印/视频读取打印/图像裁剪/颜色通道提取/边界填充/数值计算)、源码解读 openCV实战-系列教程1:基本操作(环境配置/图像读取打印/视频读取打印/图像裁剪/颜色通道…...
《华为认证》6to4自动隧道
实验需求: 在NE1和NE3之间使用tunnel 口创建6to4自动隧道,实现PC1和PC2互访。 步骤1:配置ipv4地址,如图所示: 步骤2:配置NE1和NE3的ipv4路由,是两端的ipv4网络能够互访 R1: ip route-static 0.0.0.0 0…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...
中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点
中科院1区顶刊|IF14:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点 当下,免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入,我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...
