iple变量将实例化10次。当一个变量被捕获时,捕捉的是变量的"实例"。如果在循环内捕捉multiple,第一次循环迭代时捕获的变量与第二次循环时捕获的变量是不同的。
1 List<MethodInvoke> list = new List<MethodInvoke>();
2 for (int index = 0; index < 5; index++)
3 {
4 int counter = index * 10;
5 list.Add(delegate
6 {
7 Console.WriteLine(counter);
8 counter++;
9 });
10 }
11 foreach (MethodInvoke t in list)
12 {
13 t();
14 }
15
16 list[0]();
17 list[0]();
18 list[0]();
19
20 list[1]();
输出结果:
上述代码首先创建了5个不同的委托实例,调用委托时,会先打印counter值,再对它进行递增。由于counter变量是在循环内部声明的,所以每次循环迭代,它都会被实例化。这样一来,每个委托捕捉到的都是不同的变量。
6、共享和非共享的变量混合使用:
1 MethodInvoke[] delegates = new MethodInvoke[2];
2 int outside = 0;
3
4 for (int i = 0; i < 2; i++)
5 {
6 int inside = 0;
7 delegates[i] = delegate
8 {
9 Console.WriteLine($"{outside},{inside}");
10 outside++;
11 inside++;
12 };
13 }
14
15 MethodInvoke first = delegates[0];
16 MethodInvoke second = delegates[1];
17
18 first();
19 first();
20 first();
21
22 second();
23 second();
输出结果:
首先outside变量只声明了一次,但inside变量每次循环迭代,都会实例化一个新的inside变量。这意味着当我们创建委托实例时,outside变量将由委托实例共享,但每个委托实例都有它们自己的inside变量。
7、总结:
如何合理使用捕获变量?
1)如果用或不用捕获变量的代码同样简单,那就不要用。
2)捕获由for或foreach语句声明的变量之前,思考你的委托是否需要再循环迭代结束之后延续,以及是否想让它看到那个变量的后续值。如果不是,就在循环内另建一个变量,用来复制你想要的值。
3)如果创建多个委托实例,而且捕获了变量,思考下是否希望它们捕获同一变量。
4)如果捕获的变量不会发生变化,就不需要担心。
5)如果你创建的委托实例永远不会存储别的地方,不会返回,也不会启动线程。
6)从垃圾回收的角度,思考任何捕获变量被延长的生存期。这个问题一般都不大,但假如捕获的对象会产生昂贵的内存开销,问题就会凸显出来。
参考:深入理解C#_第三版
|