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


相关推荐

  • 九九乘法表

    九九乘法表九九乘法表

    2022年4月24日
  • Java VisualVM远程监控JVM

    Java VisualVM远程监控JVM

    2022年3月12日
  • 彻底理解js中的闭包

    彻底理解js中的闭包闭包是js的一个难点也是它的一个特色,是我们必须掌握的js高级特性,那么什么是闭包呢?它又有什么用呢?我们都知道,js的作用域分两种,全局和局部,基于我们所熟悉的作用域链相关知识,我们知道在js作用域环境中访问变量的权利是由内向外的,内部作用域可以获得当前作用域下的变量并且可以获得当前包含当前作用域的外层作用域下的变量,反之则不能,也就是说在外层作用域下无法获取内层作用域下的变量,同样在不同的…

    2022年6月24日
  • ORACLE函数之日期时间运算函数[通俗易懂]

    ORACLE函数之日期时间运算函数

    2022年1月18日
  • Kong网关初探_API网关

    Kong网关初探_API网关安装Kong安装文档Kong开源版不提供dashboard支持,只有Kong企业版才有该功能。但有第三方控制台Konga同样可以友好地管理KongAdminAPI对象,快速安装如下:dockerrun-d-p1337:1337\–namekonga\–network=kong-net\-eDB_ADAPTER=postgres\-eDB_HOST=kong-database\-eDB_PORT=5432\-eDB_USER=kong\

    2022年9月10日
  • python控制mt4自动交易软件下载_MT4 EA智能自动交易系统使用教程[通俗易懂]

    python控制mt4自动交易软件下载_MT4 EA智能自动交易系统使用教程[通俗易懂]1.首先,你要有一个EA,必须要有以ex4为扩展名的,如果只有mq4文件的话,就要用MetaTrader自带的编辑器MetaEditor打开,将mq4通过编译(compile)并且要不出现错误,才能在原存放mq4的文件夹下面得到一个同名的ex4文件。2.将这个ex4文件复制到MetaTrader4所在的文件夹下面的experts文件夹下,比如:D:\ProgramFiles\ACTCMeta…

    2022年5月30日

发表回复

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

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