正确使用和理解C#中的闭包

定义

我们把在Lambda表达式(或匿名方法)中所引用的外部变量称为捕获变量。而捕获变量的表达式就称为闭包

捕获变量

捕获的变量会在真正调用委托时“赋值”,而不是在捕获时“赋值”,即总是使用捕获变量的最新的值。如下代码所示,调用委托时,age的最新值为30,所以输出的值也是30。

int age = 28;
//定义委托
Func<int, string> consoleAge = i => $"洋小豆今年{i}岁了";
age = 30;
//调用委托
string outputMsg = consoleAge(age);
outputMsg.Dump();

输出结果如下图:a91727a1b7daa4b45a95c544b2123532.png

捕获迭代变量

当捕获的外部变量为for循环的迭代变量时,C#认为变量i是定义在循环体外的。所以,当添加委托集合的for循环执行完时,i的值已经变为3了;因此,我们在foreach中循环调用委托时,i的值就都是3了。

List<Action> levyActions = new List<Action>();
for (int i = 0; i < 3; i++)
{levyActions.Add(()=> i.Dump());
}
foreach (Action action in levyActions)
{action();
}

输出结果如下图:b074cd0c1eb556035223669c7c4b9dfd.png

那么,如果我们期望输出的结果为1,2,3那需要怎么修改呢?这里我们只需要在for循环内部使用局部变量即可(每次循环捕获的是不同的变量),如下修改后的代码:

for (int i = 0; i < 3; i++)
{int tmp = i;levyActions.Add(()=> tmp.Dump());
}

输出结果如下图:

d419bd0560cf2f09568dd172e5dfb747.png

看到这里大家应该基本明白怎么回事了吧!再想下,迭代除了可以使用for还可以使用foreach啊!那么,我们把上面示例中的for循环部分改造成foreach会怎么样呢?

string[] names = new string[] {"洋小豆", "列位一分钟", "levy"};
List<Action> levyActions = new List<Action>();
foreach (string name in names)
{levyActions.Add(()=> name.Dump());
}

输出结果如下图:

86e1dbee8644f6e4011034fbf9e0cd7e.png

纳尼?输出的结果竟然跟我们上面的讲解不一样?不是应该输出捕获变量的最新值吗?应该输出3个“levy”啊!哈哈,这里是因为我的示例代码是基于.net core3.0的,从C#5.0开始,foreach认为循环变量都应该是“新”的变量。所以,每次循环中创建委托时捕获的变量都不是同一个变量。因而,输出的值肯定也就不一样了。有兴趣的童鞋可以在C#5.0之前的版本下测试下,看看输出的是不是3个“levy”。

背后原理

分析IL代码我们可以得知,编译器在背后生成了一个私有的密封类c__DisplayClass4_0,它将外部变量包装成类的成员变量,而委托方法包装成类的方法。所以,上述捕获for迭代变量的示例代码就可以修改成如下:

void Main()
{List<Action> levyActions = new List<Action>();c__DisplayClass4_0 local = new c__DisplayClass4_0();for (local.i = 0; local.i < 3; local.i++){levyActions.Add(() => local.Main_b__0());}foreach (Action action in levyActions){action();}
}private sealed class c__DisplayClass4_0
{public int i;internal void Main_b__0(){i.Dump();}
}

涉及到外部变量i的地方都替换成了local.i,所以,每次循环修改的始终是c__DisplayClass4_0的成员变量i的值,那么调用委托时输出的自然都是3了。

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

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

相关文章

重磅公开!36个高考数学破题大招

前段时间&#xff0c;超模君发了关于极值点偏移问题的总结资料&#xff0c;朋友们都觉得内容整合得十分详细。同时&#xff0c;也有不少很多朋友留言说希望有其他高考数学常考问题的解题总结。确实&#xff0c;毕竟高考涉及的内容非常多&#xff0c;比如说参数范围问题、数列求…

Linux服务器数据备份

2019独角兽企业重金招聘Python工程师标准>>> 服务器上的数据是如此重要&#xff0c;以至于我们定期要对其进行备份&#xff01;这里讲一下mysql的备份和使用rsync同步服务器上的文件&#xff0c;从而实现多台linux服务器之间的文件的备份。 一 Mysql备份 mysql的备份…

与毒”共舞30年!清华美女研究生为何放弃高薪,选择特招入伍?背后的原因令人泪崩......

全世界只有3.14 % 的人关注了爆炸吧知识从武汉新冠疫情爆发到如今&#xff0c;陈薇没有一天休息。短短半年间&#xff0c;54岁的她头发从黑到白&#xff0c;也哭了好几次。刚去武汉-现在陈薇的母亲也在电视上看到了女儿的变化&#xff1a;“她变老了&#xff0c;都有白头发了。…

Centos-启动network报错RTNETLINK answers: File exists解决方法

背景&#xff1a;今天在Vcenter上&#xff0c;用模板克隆了一个虚拟机&#xff0c;启动之后&#xff0c;网卡启动不了&#xff0c;报错如下&#xff1a;RTNETLINK answers: File exists说明&#xff1a; 环境 Centos6.6_X64 &#xff1b; 网卡两个原因&#xff1a; 由于用模板…

不当败家子的原因......

1 实在是太真实&#xff01;▼2 有钱就是可以为所欲为&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 在哪都要有仪式感&#xff01;▼4 送自己去孤儿院......▼5 万万没想到...&#xff08;图源网络&#xff0c;侵删&#xff09;▼6 这都能催的&#xff1f;▼…

我不信奉Scrum,我信奉敏捷

Scrum一直以来争论不断。虽然创始人Ken在演讲中曾说过即使是白痴也可以用Scrum&#xff0c;但是依然有很多人认为Scrum对团队成员的素质要求非常高。另据统计&#xff0c;75%以上的Scrum都可以称得上失败。 去年十月&#xff0c;有幸参加了Outsofting鲍央舟老师的Scrum培训。培…

仿京东商城源码java_Java+SSM实现类似京东的3C电子商城系统

需求分析基于Spring, SpringMVC, Mybatis 实现一个类似仿京东商城的3C电子商城系统, 能够实现商品管理与展示, 加入购物车, 支付购买等功能, 项目采用java技术进行开发。运行环境java, jdk1.8,tomcat8.5,mysql5.6,EclispseEE项目技术java, spring springmvc, mybatis, bootstra…

跨浏览器开发:CSS代码的金科玉律

作为Web设计师&#xff0c;你的网站在各种浏览器中有完全一样的表现是很多人的目标&#xff0c;然而这是一个永远无法真正实现的目标&#xff0c;很多人认为&#xff0c;完美的跨浏览器兼容并不必要&#xff0c;这样说虽然没错&#xff0c;但在很多情形&#xff0c;一种近似的兼…

Refit结合Polly访问ASP.NET Core Web API

前言在.NET Core应用中访问ASP.NET Core Web API接口&#xff0c;常用的方式是使用IHttpClientFactory生成HttpClient实例&#xff0c;并通过结合Polly策略&#xff0c;以实现重试&#xff0c;熔断等机制。在本文中&#xff0c;我们将介绍如何使用Refit&#xff0c;结合Polly访…

10个经典的爆炸化学反应,个个都是你惹不起的“暴脾气”

全世界只有3.14 % 的人关注了爆炸吧知识没有放过火&#xff0c;没有爆炸过&#xff0c;就不算真正学化学的。还有谁能比化学狗们的胆子更大、回忆更深刻吗&#xff1f;化学狗们能活到现在简直是个奇迹&#xff5e;&#xff5e;&#xff5e;懂的就默默转了吧&#xff01;小编整理…

狗为什么吃屎?真相男默女泪,但看完后你也会流口水......

全世界只有3.14 % 的人关注了爆炸吧知识别人笑狗太疯癫狗笑他人看不穿据说&#xff0c;200%的网友在学习一门新语言时&#xff0c;都会从最基本的日常问候语开始学起&#xff0c;例如——骂人。危险动作&#xff0c;请勿模仿。那但凡是学过汉语的人应该都知道&#xff0c;咱们中…

WPF开发登录窗口之——添加文本输入框用户控件

WPF开发者QQ群&#xff1a; 340500857 | 微信群 目前人数太多&#xff0c;暂不开放窗口开发完成后&#xff0c;接下来就是开发客户区中的输入框控件&#xff0c;但在开发之前&#xff0c;我们先开发一个输入框基类&#xff0c;然后通过继承的方式去实现文本输入框与密码输入框…

JSTL分割字符 fn:split()

2019独角兽企业重金招聘Python工程师标准>>> <% page language"java" contentType"text/html; charsetUTF-8"%> <% taglib uri"http://java.sun.com/jsp/jstl/core" prefix"c"%> <% taglib uri"http:/…

女友的前男友教我该怎么照顾她......

1 总觉得不太对劲&#xff1f;▼2 这回复真神&#xff01;&#xff08;via.机智得赵老师&#xff09;▼3 奇怪的知识又增加了......▼4 卖茶小妹真的与时俱进...&#xff08;via.外卖小哥金城武&#xff09;▼5 超大容量花露水没见过吧&#xff1f;&#xff01;▼6 别人在…

浅谈.Net异步编程的前世今生----EAP篇

前言在上一篇博文中&#xff0c;我们提到了APM模型实现异步编程的模式&#xff0c;通过使用APM模型&#xff0c;可以简化.Net中编写异步程序的方式&#xff0c;但APM模型本身依然存在一些缺点&#xff0c;如无法得知操作进度&#xff0c;不能取消异步操作等。针对这些缺点&…

CentOS 5 CentOS 6 启动流程及关键步骤

CentOS 5 CentOS 6 启动流程及关键步骤1、加电自检&#xff1a;当打开主机电源时&#xff0c;主机会唤醒cpu&#xff0c;使其运行CMOS中的BIOS&#xff0c;BIOS检查必要的硬件是否存在&#xff08;内存、硬盘等&#xff09;BIOS其实是一个小型系统&#xff0c;可以完成一些像检…

建议给予导师决定硕博士能否毕业的自主权?教育部:将充分采纳!

全世界只有3.14 % 的人关注了爆炸吧知识本文来源&#xff1a;教育部 近日&#xff0c;教育部官网公布了《对十三届全国人大三次会议第9546号建议的答复》。针对人大代表提出的关于完善高校研究生科研成果评价标准的建议&#xff0c;教育部作出回应&#xff0c;并透露了对加强研…

浅谈.Net异步编程的前世今生----APM篇

前言在.Net程序开发过程中&#xff0c;我们经常会遇到如下场景&#xff1a;编写WinForm程序客户端&#xff0c;需要查询数据库获取数据&#xff0c;于是我们根据需求写好了代码后&#xff0c;点击查询&#xff0c;发现界面卡死&#xff0c;无法响应。经过调试&#xff0c;发现查…

浅谈.Net异步编程的前世今生----TPL篇

前言我们在此前已经介绍了APM模型和EAP模型&#xff0c;以及它们的优缺点。在EAP模型中&#xff0c;可以实时得知异步操作的进度&#xff0c;以及支持取消操作。但是组合多个异步操作仍需大量工作&#xff0c;编写大量代码方可完成。因此&#xff0c;在.Net Framework 4.0中&am…

Google:推荐几款好用的Chrome浏览器插件

1、Clear Cache 0.3.3.2 一键清空浏览器缓存数据。 https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn 2、Pig Toolbox 1.0.6.4 双击关闭页签&#xff0c;鼠标手势&#xff0c;手势动作轮&#xff0c;摇臂&#xff0c;超级拖拽&#xf…