2.循环内的被捕获的变量。
首先看一段代码:
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++。
同时可以看到new DisplayClass1的位置在循环内部
对应的C#代码我想是这样的
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。如下代码将证实这一点: 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表达式的时候需要注意的地方。
转载于:https://www.cnblogs.com/LoveJerryZhang/archive/2012/12/04/2800651.html
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/110270.html原文链接:https://javaforall.cn