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

news2024/10/7 12:29:27

在这里插入图片描述

文章目录

  • 匿名函数
    • 匿名函数的定义
    • 匿名函数作为参数传递
    • 匿名函数的缺点
  • 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.coloradmin.cn/o/912857.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity3D赛车游戏制作】初步导入,资源很哇塞【一】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

位运算相关总结

371. 两整数之和 给你两个整数 a 和 b &#xff0c;不使用 运算符 和 - ​​​​​​​&#xff0c;计算并返回两整数之和。 class Solution { public:int getSum(int a, int b) {while (b ! 0) {// 计算进位&#xff1a;将 a 和 b 进行位与操作&#xff0c;然后左移 1 位。u…

ExoPlayer如何使用MediaExtractor的思路

本文主要针对于&#xff0c;自己的设备解码能力&#xff08;比如底层集成ffmpeg 、qti、android 、需要付费的格式等等&#xff09;大于ExoPlayer自己封装的固有Extractor&#xff0c;基于现在Android架构通俗的来说&#xff0c;就是MediaPlayer可以播&#xff0c;但是ExoPlaye…

解码客厅:知名设计师带你探索其历史与设计风格

会客厅又称接待室&#xff0c;&#xff0c;它们是宾客和家人享受下午或晚上娱乐时光的天然聚会场所。由于会客厅反映了每个家庭的个性&#xff0c;因此在家具和设计上花费了很多心思。装饰品、复古艺术品、三角钢琴以及雕塑和花瓶等其他装饰元素在今天的会客厅中已司空见惯。 下…

UE4 地形编辑基础知识 学习笔记

之前自己写过这样的功能&#xff0c;今天看到一个UE现成的 点击地形&#xff0c;选择样条 按住CTRL键点击屏幕中某一个点会在场景内生成一个这样的图标 再点两次&#xff0c;会生成B样条的绿线条 点击号再选择一个模型&#xff0c;会生成对应的链条状的mesh 拉高最远处的一个图…

2023国赛数学建模思路 - 案例:粒子群算法

文章目录 1 什么是粒子群算法&#xff1f;2 举个例子3 还是一个例子算法流程算法实现建模资料 # 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法&#xff1f; 粒子群算法&#xff08;Pa…

无涯教程-PHP - preg_split()函数

preg_split() - 语法 array preg_split (string pattern, string string [, int limit [, int flags]]); preg_split()函数的操作与split()完全相同&#xff0c;只不过正则表达式被接受为pattern的输入参数。 如果指定了可选的输入参数limit&#xff0c;则仅返回子字符串的限…

测试框架pytest教程(11)-pytestAPI

常量 pytest.__version__ #输出pytest版本 pytest.version_tuple #输出版本的元组形式 功能 pytest.approx pytest.approx 是一个用于进行数值近似比较的 pytest 断言工具。 在测试中&#xff0c;有时候需要对浮点数或其他具有小数部分的数值进行比较。然而&#xff0c;由于…

正中优配:散户也算股东吗?能不能参加股东大会?

股市上&#xff0c;常有散户自称韭菜&#xff0c;长一波被股市收割一波&#xff0c;不可谓不惨。但要较真的话&#xff0c;散户仍是有必定身份的&#xff0c;最少他是所持股的股东。有人会好奇&#xff0c;原来散户也算股东吗&#xff1f;那他能不能参与公司的股东大会&#xf…

一篇文章教你自动化测试如何解析excel文件?

前言 自动化测试中我们存放数据无非是使用文件或者数据库&#xff0c;那么文件可以是csv&#xff0c;xlsx&#xff0c;xml&#xff0c;甚至是txt文件&#xff0c;通常excel文件往往是我们的首选&#xff0c;无论是编写测试用例还是存放测试数据&#xff0c;excel都是很方便的。…

怎么测试app?app的测试技巧是什么?

前言 今天笔者想和大家来唠唠app测试&#xff0c;现在的app有非常的多&#xff0c;这些app都是需要经过测试之后才能发布到应用市场中&#xff0c;app已经成为了我们日常生活中不可或缺的一部分了&#xff0c;但它的功能必须强大&#xff0c;才能受到消费者的重视&#xff0c;并…

深度学习3:激活函数

一、激活函数的简介与由来 激活函数&#xff1a;是用来加入非线性因素的&#xff0c;解决线性模型所不能解决的问题。 线性函数的组合解决的问题太有限了&#xff0c;碰到非线性问题就束手无策了。如下图。 通过激活函数映射之后&#xff0c;可以输出非线性函数。 最后再通过…

Ubuntu下跑Aplaca报错:torch.cuda.0utofMemoryError: CUDA out of memory.解决办法(查看CUDA占用情况清除GPU缓存)

缓存不够&#xff01;&#xff01;&#xff01;&#xff01;并非内存容量不够 错误提示&#xff1a; torch.cuda.0utofMemoryError: CUDA out of memory.Tried to allocate 2.00 MiB (PU 0; 23.69 GiB total capacity; 237 BiB already allocated; 18.38 MiB fre; 2.50 GiB re…

解决elementUI打包上线后icon图标偶尔乱码的问题

解决vue-elementUI打包后icon图标偶尔乱码的问题 一、背景二、现象三、原因四、处理方法方式1&#xff1a;使用css-unicode-loader方式2&#xff1a;升高 sass版本到1.39.0方式3&#xff1a;替换element-ui的样式文件方式4&#xff1a;更换打包压缩方式知识扩展&#xff1a;方式…

一文读懂AUTOSAR系列 – DEM功能详解

摘要&#xff1a; DEM是以DTC为核心的AUTOSAR基础软件模块&#xff0c;实现了对DTC的监控上报&#xff0c;存储等功能&#xff0c;如果需要对AUTOSAR诊断进行进一步学习&#xff0c;还需要对DCM&#xff0c;Doip&#xff0c;Cantp等模块进行系统性学习。 前言 Dem是AUTOSAR中配…

【MySQL系列】ALTER语句详解,以及UPDATE,DELECT,TRUNCATE语句的使用+区别

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

网络货运平台盈利指南

8月17日&#xff0c;由中国物流与采购联合会主办的2023&#xff08;第十五届&#xff09;物流与供应链数字化发展大会在贵阳成功举办&#xff01; 会议期间&#xff0c;数据宝作为中物联副会长单位受邀出席&#xff0c;并与菜鸟集团、中储、满帮、华润、货运中国、山东高速、德…

玩转自动化测试:Monkey工具的实践应用

本文将介绍monkey在自动化测试中的使用技术&#xff0c;主要包括monkey的安装和配置&#xff0c;使用monkey进行自动化测试的步骤&#xff0c;以及monkey的常用选项和技巧 01、Monkey的安装和配置 Mac OS X 在Mac OS X中&#xff0c;可以通过终端进行monkey的安装和配置 安…

4.物联网LWIP之C/S编程,实现服务器大小写转换

LWIP配置 服务器端实现 客户端实现 错误分析 一。LWIP配置&#xff08;FREERTOS配置&#xff0c;ETH配置&#xff0c;LWIP配置&#xff09; 1.FREERTOS配置 为什么要修改定时源为Tim1&#xff1f;不用systick&#xff1f; 原因&#xff1a;HAL库与FREERTOS都需要使用systi…

资损防控技术体系简介及实践

1 资损防控介绍 得物提供大量商品买卖等服务&#xff0c;资金流转量大&#xff0c;任何由于设计缺陷、系统缺陷、系统故障、人为操作、安全漏洞等因素都会引发直接或间接资金损失。资损防控就是在项目全生命周期内&#xff0c;引入多种资金分析和控制手段&#xff0c;预防资损…