由于这里没有人直接引用ECMA-334:
10.4.4.10用于语句
for语句的明确赋值检查:
for (for-initializer; for-condition; for-iterator) embedded-statement
就好像语句是这样写的:
{
for-initializer;
while (for-condition) {
embedded-statement;
LLoop: for-iterator;
}
}
在说明书中,
12.16.6.3局部变量的实例化
当执行进入局部变量的作用域时,就认为局部变量已实例化。
[示例:例如,当调用下面的方法时,局部变量x将被实例化和初始化三次——每次循环迭代一次。
static void F() {
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
然而,将x的声明移到循环之外会导致x的单个实例化:
static void F() {
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
最后的例子)
如果没有捕获,则无法准确地观察局部变量实例化的频率——因为实例化的生存期是不相连的,因此每个实例化可能只是使用相同的存储位置。然而,当匿名函数捕获局部变量时,实例化的效果就变得明显了。
[示例:示例。
using System;
delegate void D();
class Test{
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
static void Main() {
foreach (D d in F()) d();
}
}
产生输出:
1
3
5
然而,当x的声明被移到循环之外时:
static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = () => { Console.WriteLine(x); };
}
return result;
}
输出结果为:
5
5
5
请注意,编译器允许(但不要求)将三个实例优化为单个委托实例(§11.7.2)。
如果for循环声明了一个迭代变量,则该变量本身被认为是在循环之外声明的。
[示例:因此,如果将示例更改为捕获迭代变量本身:
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
result[i] = () => { Console.WriteLine(i); };
}
return result;
}
只有一个迭代变量的实例被捕获,它产生输出:
3
3
3
最后的例子)
哦,是的,我想应该提到的是,在c++中这个问题不会发生,因为你可以选择是通过值捕获变量还是通过引用捕获变量(参见:Lambda捕获)。