防止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)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • Docker暴露2375端口导致服务器被攻击解决方法!

    Docker暴露2375端口导致服务器被攻击解决方法!相信了解过dockerremoteAPI的同学对2375端口都不陌生了,2375是docker远程操控的默认端口,通过这个端口可以直接对远程的dockerdaemon进行操作。当$HOST主机以dockerdaemon-H=0.0.0.0:2375方式启动daemon时,可以在外部机器对$HOST的dockerdaemon进行直接操作:docker-Htcp://$HOS…

    2022年4月29日
  • php取得json_decode中的值,php json decode-获取值[通俗易懂]

    php取得json_decode中的值,php json decode-获取值[通俗易懂]正如Danp已经说过的,返回的JSON包含在函数调用中(由jsoncallback=json)你不能完全摆脱这个,但是,只是用AreaSearch?jsoncallback=&lat=41.1131514&lng=-74.0437521至少删除json在字符串的开头,您可以通过以下方式除去括号:$json=trim(trim($json),”();”);给予:{items:[…

    2022年7月17日
  • USB转232和485的区别

    1串口串口,即串行通信接口,与之相对应的另一种接口叫并口,并行接口。两者的区别是,传输一个字节(8个位)的数据时,串口是将8个位排好队,逐个地在1条连接线上传输,而并口则将8个位一字排开,分别在8条连接线上同时传输,也就是进行数据传输的接口串口是一种物理接口形式,(硬件)通常指COM接口,当然这些接口有着很多标准接口标准:串口通信的接口标准有很多,而我们所了解的RS-23…

    2022年4月7日
  • SpringMVC 查询参数接收时间戳转Date方法[通俗易懂]

    SpringMVC 查询参数接收时间戳转Date方法[通俗易懂]Springmvc通过@RequestParam接收参数指定类型为Date时,接口传入时间戳会默认为string类型,无法转为Date,可在controller中添加如上转换器。@InitBinderpublicvoidinitBinder(finalWebDataBinderwebdataBinder){webdataBinder.reg…

    2022年6月18日
  • ROC曲线及AUC值[通俗易懂]

    ROC曲线及AUC值[通俗易懂]ROC曲线参考文献:【ROC曲线与AUC值】1.介绍及引入ROC的全名叫做ReceiverOperatingCharacteristic,其主要分析工具是一个画在二维平面上的曲线——ROCcurve。平面的横坐标是falsepositiverate(FPR),即假阳性率(1−Sp1-Sp1−Sp);纵坐标是truepositiverate(TPR),即真阳性率(SnSnSn)。对某个分类器而言,我们可以根据其在测试样本上的表现得到一个TPR和FPR点对。这样,此分类器就可以映射成

    2022年5月16日
  • MATLAB函数调用

    MATLAB函数调用数学建模matlab自定义函数时间2020年5月10日学习Matlab自定义函数使用,并结合所学函数简单修改了一下上周的代码实现了Topsis法。1.Matlab语句构成的程序文件称为M文件,以m作为文件的扩展名,分为函数文件和程序文件。程序文件即脚本文件,无function;函数文件有function,且在第一行或者第一个不是注释的行2.两个文件运行函数:函数定义文件和函数调用文件(函数可在脚本文件或命令窗口直接调用),两文件必须放在同一目录下,函数文件名必须与函数名相…

    2022年7月17日

发表回复

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

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