You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 21:47:04