一个感觉难以理解的的 C#代码片段,想知道这是为什么
代码如下:
Action[] foos = new Action[4]; for (int i = 0; i < 4; i++) { funcs[i] = () => Console.Write($"{i} "); } foreach (var foo in foos) { foo(); }
输出如下:
4 4 4 4
为什么输出不是0 1 2 3而是4 4 4 4呢
代码如下:
Action[] foos = new Action[4]; for (int i = 0; i < 4; i++) { funcs[i] = () => Console.Write($"{i} "); } foreach (var foo in foos) { foo(); }
输出如下:
4 4 4 4
为什么输出不是0 1 2 3而是4 4 4 4呢
internal void <M>b__0()
{
Console.Write(string.Format(“{0} “, i));
}
}
public void M()
{
Action[] array = new Action[4];
a_0 a_ = new a_0();
a_.i = 0;
while (a_.i < 4)
{
array[a_.i] = new Action(a_.<M>b__0);
a_.i++;
}
Action[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
array2[i]();
}
}
注:为便于观看有改名;编译器实现,准确见标准;来自 sharplab.io ;
我就排个队,看看各位如何解释。
不过好奇为啥是这种实现方式(四个 lambda 只 new 一个类)而不是 new 4 个类。有大佬解释一下吗?
//0 1 2 3
for (init; cond; next) body;
{ // 1
init;
while (cond)
{ // 2
body;
goto_continue:
next;
} // 2
} // 1
循环变量 i 的 scope 是 1,而 scope 1 只进入了一次,所以只有一个状态对象。
如果你改成
foreach (int i in new int[] { 0,1,2,3 })
则在较新的 C# 编译器下会得到 0 1 2 3,因为这等价于
{ // 1
var coll = new int[] { 0,1,2,3 };
for (int idx = 0; idx < coll.Length; ++idx)
{ // 2
int i = coll[idx];
foos[i] = () => Console.WriteLine(i);
} // 2
} // 1
因为 i 的 scope 变成了 for 的里面,所以会进入 4 次,因此有 4 个不同的状态对象被创建,每个 Action 都会引用各自状态对象的方法。
12. Expressions
12.16 Anonymous function expressions
12.16.6 Outer variables
If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop.
=> i 在 for 外
Any local variable, value parameter, or parameter array whose scope includes the lambda-expression or anonymous-method-expression is called an outer variable of the anonymous function.
=> i 也在匿名函数外
A local variable is considered to be instantiated when execution enters the scope of the variable.
=> i 仅一实例
When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function.
=> 匿名函数用了那唯一 i
标准里的例子:
░░static D[] F() {
░░░░D[] result = new D[3];
░░░░for (int i = 0; i < 3; i++) {
░░░░░░result[i] = () => { Console.WriteLine(i); };
░░░░}
░░░░return result;
░░}
only one instance of the iteration variable is captured, which produces the output:
░░3
░░3
░░3
https://www.jetbrains.com/help/resharper/AccessToModifiedClosure.html
等价于
Action[] foos = new Action[4];
int I;
for (i = 0; i < 4; i++)
{
funcs[i] = () => Console.Write($”{i} “);
}