为何C#循环中委托会输出十次10?循环变量生命周期解惑
C#循环委托捕获变量问题解析
为什么所有委托都输出10?
这是因为不同C#版本对for循环变量的处理逻辑不同:
- 在C# 5.0之前,for循环的变量
i本质是在循环外部声明的(编译后实际代码逻辑如此)。所有添加到列表的委托都会捕获同一个i的引用,而非每次循环的i值。当循环结束时,i的值已经变成了10,所以执行所有委托时,都会输出这个最终值。
对应的示例代码:List<Action> actions = new List<Action>(); for (int i = 0; i < 10; i++) { actions.Add(() => Console.WriteLine(i)); } foreach (var action in actions) { action(); // 十次输出10 } - 在C# 5.0及之后,编译器做了优化:每次循环迭代都会创建一个新的
i变量实例。每个委托捕获的是当前迭代的i实例,所以执行时会依次输出0到9。
作用域与生命周期的差异
- 作用域:指变量在代码中可被直接访问的区域。比如旧版代码里,你不能在for循环外部直接写
Console.WriteLine(i)(编译器会报错),这就是作用域的限制。但委托捕获的是变量的内存引用,不受语法作用域的直接约束。 - 生命周期:指变量在内存中存在的时长。C#的垃圾回收器(GC)会跟踪变量的引用情况,只要有活跃的引用存在(比如列表里的委托还持有对变量的引用),变量就不会被回收,生命周期会超出它的语法作用域。这就是为什么你的代码不会抛出“变量不存在”的异常——
i对应的内存实例还被委托引用着,一直存活。
变量i的存活不是靠引用计数
C#默认的垃圾回收机制是分代标记-清除算法,而非引用计数(仅少数场景如COM对象会用到引用计数)。当委托捕获循环变量i时,编译器会自动生成一个匿名类,将i作为该类的字段存储。委托实际上引用的是这个匿名类的实例,只要列表中的委托还存在,这个匿名类实例就有活跃引用,GC不会回收它,因此i(作为类的字段)也会一直保存在内存中。
内容的提问来源于stack exchange,提问作者Aaras
相关产品推荐
相关产品推荐

