由于没有人直接引用ECMA-334,因此在这里说明:
10.4.4.10 For语句
检查具有以下形式的for语句的明确赋值情况:
for (for-initializer
就好像该语句已经写成一样完成了:
{
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();
}
}
produces the output:
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;
}
the output is:
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 capture)。