防止Lambda的各种坑爹(二)

防止Lambda的各种坑爹(二)

  2.循环内的被捕获的变量。

  首先看一段代码:

防止Lambda的各种坑爹(二)
防止Lambda的各种坑爹(二)
View Code

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Lambda2
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             List<Action> list = new List<Action>();
14             for (int i = 0; i < 3; i++)
15             {
16                 int current = i;
17                 list.Add(() =>
18                 {
19                     Console.Write(current);
20                     current++;
21                 });
22             }
23 
24             foreach (var item in list)
25             {
26                 item();
27             }
28             list.First()();
29             list.First()();
30 
31             Console.Read();
32         }
33     }
34 }

  你能猜出输出的是什么吗?如果你的答案是01212,那么恭喜你,你的答案是正确的。这里可以看出:当在Lambda中捕获一个变量时,被捕获的是变量的实例。也就是说,循环第一次捕获的变量将有别与循环第二次捕获的变量,就像有3个current变量一样,全部叫做current,他们一个接一个的创建。

  代码会创建3个不同的委托—每次循环都会创建一个,添加到一个List集合中。现在,由于current变量是在循环内声明的,所以每次循环迭代。他都会被创建。这样每次委托捕获到的都是不同的current变量的值。所以一次调用每个委托。输出的结果依次是0 1 2。然后我们在执行2个第一个委托,由于在执行了current++,所以依次再输出 1 2。

  我想你一定不奇怪为什么每次的current变量的值不同,因为这个看上次似乎是理所当然的。是这样吗?我们通过ILDASM查看对应生成的IL代码:发现编译器依旧为我们生成的了一个类DisPlayClass1,这个类包装了current变量和委托包装的方法 <Main>b__0:void: Console.Write(current);current++。

防止Lambda的各种坑爹(二)

  同时可以看到new DisplayClass1的位置在循环内部

防止Lambda的各种坑爹(二)

  对应的C#代码我想是这样的

防止Lambda的各种坑爹(二)
防止Lambda的各种坑爹(二)
View Code

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Lambda2
 8 {
 9     class Program
10     {
11         private class DisplayClass1
12         {
13             public int current;
14 
15             public void F()
16             {
17                 Console.Write(current);
18                 current++;
19             }
20         }
21 
22         static void Main(string[] args)
23         {
24 
25             List<Action> list = new List<Action>();
26             for (int i = 0; i < 3; i++)
27             {
28                 DisplayClass1 d = new DisplayClass1();
29                 d.current = i;
30                 list.Add(d.F);
31             }
32 
33             foreach (var item in list)
34             {
35                 item();
36             }
37             list.First()();
38             list.First()();
39 
40             Console.Read();
41         }
42     }
43 }

  好了,这样的话就有了一个新问题,应该思考的一点是,如果我们移除current变量,直接用for循环中的i的代替的话,那么会发生什么呢?在这种情况下,所以的循环内的委托共享的是一个变量i。输出的将是3 4 5 6 7。之所以这样,是因为在循环结束时,i的值是3(同时要注意的是,委托内的i++不会现在执行)。之后的每次调用委托都会使i++,每个委托都是调用的同一个变量i。如下代码将证实这一点:

防止Lambda的各种坑爹(二)
防止Lambda的各种坑爹(二)
View Code

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Lambda2
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13 
14             List<Action> list = new List<Action>();
15             for (int i = 0; i < 3; i++)
16             {
17                 list.Add(() =>
18                 {
19                     Console.Write(i);
20                     i++;
21                 });
22             }
23 
24             foreach (var item in list)
25             {
26                 item();
27             }
28             list.First()();
29             list.First()();
30 
31             Console.Read();
32         }
33     }
34 }

  同时看到对应的IL代码,new DisplayClass1的位置在循环外部

防止Lambda的各种坑爹(二)

  好了,这个提醒我们以后在循环内部使用Lambda表达式的时候需要注意的地方。

转载于:https://www.cnblogs.com/LoveJerryZhang/archive/2012/12/04/2800651.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/110270.html原文链接:https://javaforall.net

(0)
上一篇 2021年8月20日 下午9:00
下一篇 2021年8月20日 下午9:00


相关推荐

  • 进销存管理系统【源码开放】[通俗易懂]

    进销存管理系统【源码开放】[通俗易懂]进销存管理系统的功能需求:1,实现采购订单的持久化,对采购商品入库处理,还有就是采购的退货处理;2,实现商品的入库、出库操作,查询商品的库存信息,修改商品的仓库号3,实现销售订单的添加,销售发货处理,并且销售的退货处理4,实现新建员工培训信息和查询员工培训记录功能5,实现对商品、供应商、客户资料的管理,对员工用户的管理,最重要的是对系统数据的备份和恢复代码的截图如下所示:系统的截图如下所示:bean层manage的代码如下所示:packag.

    2022年5月31日
    31
  • c#防止代码被反编译_C程序反编译

    c#防止代码被反编译_C程序反编译1.在编码过程中尽量使用private/internal关键词修饰class、方法和字段名称2.编码过程尽可能少地使用public修饰class、方法和字段名称3.避免使用反射和序列化,反序列化操作4.添加生成事件,调用Dotfuscator进行代码混淆if$(ConfigurationName)==Debug”C:/ProgramFiles(x86)/MicrosoftVisualStudio14.0/PreEmptiveSolutions/Dotfuscatora

    2025年8月28日
    14
  • maven打包命令[通俗易懂]

    maven打包命令[通俗易懂]打包步骤是:清除,打包一般的maven项目打包命令是:mvncleanpackagemvnclean打包前清理掉之前有过打包的文件夹targetmvncompile项目编译mvnpackage开始项目打包执行这步可以不用执行compile了package包括了编译、打包这两步package完成后,会自动生成一个target文件,根据你编写的pom打包命令和assemb…

    2022年4月29日
    178
  • 【基础知识】4、python-opencv 入门基础知识

    【基础知识】4、python-opencv 入门基础知识和 Python 一样 当前的 OpenCV 也有两个大版本 OpenCV2 和 OpenCV3 相比 OpenCV2 OpenCV3 提供了更强的功能和更多方便的特性 不过考虑到和深度学习框架的兼容性 以及上手安装的难度 这部分先以 2 为主进行介绍 如果不考虑视频应用 以上三个就是最核心和常用的模块了 NearestNeigh 用于在多维空间进行聚类和检索 经常和关键点匹配搭配使用 regularizati 算法从使用的角度来看 和 OpenCV2 相比 OpenCV3 的主要变化是更多的功能和更细化的模块划分

    2026年3月17日
    2
  • 1000个微信小程序源码分享[通俗易懂]

    1000个微信小程序源码分享[通俗易懂]文章目录微信小程序源代码获取开发账号注册小程序部分源码展示程序展示微信小程序现在的微信小程序非常火爆,网上也有很多学习资源,但是源码资源还是很少的。其实在学习开发微信小程序的时候如果有源码可以供我们借鉴,学习效率也会成倍的增加。无论是前端开发,还是后端开发,时间长了,你总会能总结出它的一些规律的,对于前端开发主要就两条,页面展现,逻辑处理。如果是全流程开发的话,那就是,如何创建项目,页面如何实现,数据获取和逻辑处理如何实现,如何打包上线。移动端或者前端,基本开发流程就这个四个步骤。所以在在前端方面去学

    2025年12月14日
    6
  • 刀塔霸业未能连接到服务器,刀塔霸业进不去怎么解决 进不去解决方法详解[多图]…

    刀塔霸业未能连接到服务器,刀塔霸业进不去怎么解决 进不去解决方法详解[多图]…刀塔霸业有很多玩家爱下好游戏之后却进不去,不知道什么原因,下面安族小编带大家来看一下进不去解决方法详解,相信对大家会有帮助。刀塔霸业玩不了解决办法1、游戏需要的手机配置iOS系统需要至少1.5内存,因此iPhone6/6PLUS无法运行,需要更新的机型才可以安卓版的话,目前由于安卓适配原因,运行效果都比较差,骁龙835处理器以下的用户可能运行情况不好,这个可以等待后续官方的优化。2、已发生错误I…

    2022年5月17日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号