34.闭包中的变量捕获结果
34.1 题目
static void Main(string[] args)
{
Action action = null;
for (int i = 0; i < 10; i++)
{
action += () =>
{
Console.WriteLine(i);
};
}
action();
}
请问上面代码的最终打印结果是什么?为什么?如果我们希望打印出0~9,应该如何修改代码?
34.2 深入解析
上述代码的最终打印结果是全都是10。
这是因为在 C# 中,闭包捕获的是变量 i 本身(编译器生成的闭包类字段语义),而非循环每次迭代的瞬时值。循环结束后 i 为 10,故所有委托执行时看到的都是 10。
如果我们希望打印出0~9,可以用一个临时变量记录。
static void Main(string[] args)
{
Action action = null;
for (int i = 0; i < 10; i++)
{
int j = i;
action += () =>
{
Console.WriteLine(j);
};
}
action();
}
每次循环进入新的 int j = i 时,都会为当前迭代产生独立的局部变量;各委托捕获各自的 j,action() 调用时会依次打印 0~9。
34.3 答题示例
上述代码的最终打印结果是连续输出10个”10”。
原因是C#中闭包会捕获外部变量的引用而非值。循环中的匿名方法捕获的是变量
i的引用,而非每次循环时i的具体值。当循环结束后,i的值已自增到10,因此调用action()时,所有匿名方法都会打印此时i的引用值10。若要打印0~9,需让每个匿名方法捕获当前循环的
i值,可在循环内定义临时变量存储当前i的值,使闭包捕获临时变量的引用。修改如下:
在循环体内添加int j = i;,然后匿名方法中打印j。此时每个j都是当前循环的i值的拷贝,循环结束后各自保留对应的值,调用时会依次打印0到9。
34.4 关键词联想
闭包(Closure)- 变量引用捕获
- Action委托
- 匿名方法
循环变量(i)临时变量(值拷贝)- 变量作用域
- 委托调用时机
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com