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

遍历List<Action>时出现意外输出的原因咨询——C#面试疑难代码解析

为什么两段类似的Lambda代码输出结果不一样?

我正在搜集有挑战性的面试代码片段,发现了两段无法解释的代码,于是将它们合并以便同时运行。代码如下:

using System.Collections.Generic;
public class Program {
    public static void Main(string[] args) {
        var intActions = new List<Action>();
        for (int i = 0; i < 4; i++)
            intActions.Add(() => { Console.WriteLine(i); });
        foreach (var action in intActions)
            action();
            
        string[] strings = { "abc", "def", "ghi" };
        var stringActions = new List<Action>();
        foreach (string str in strings)
            stringActions.Add(() => { Console.WriteLine(str); });
        foreach (var action in stringActions)
            action();
    }
}

实际输出为:4 4 4 4 abc def ghi。我原本预期的结果要么是四个“4”加四个“ghi”,要么是“0 1 2 3”加“abc def ghi”,请问为何实际输出会是这样?


问题根源:闭包对变量的捕获方式差异

这其实是C#里闭包捕获变量的经典面试坑,核心在于闭包捕获的是「变量本身」,而不是变量当时的取值,但for和foreach循环在变量声明逻辑上有本质区别:

1. 第一个for循环的情况

在for (int i = 0; i < 4; i++)里,变量i是整个循环共用的——逻辑上它只被声明了一次,每次迭代只是修改它的值。当你把() => { Console.WriteLine(i); }添加到列表时,每个Lambda表达式都捕获了同一个i变量的引用。

等你执行这些Action的时候,for循环早就跑完了,此时i的值已经变成了4(循环结束的触发条件是i >=4),所以所有Action执行时都会读取到这个最终值,输出四次4。

2. 第二个foreach循环的情况

在C# 5及以后的版本中,foreach循环的行为做了针对性优化:每次迭代都会创建一个全新的str变量。也就是说,你添加到stringActions里的每个Lambda表达式,捕获的都是当前迭代专属的独立str变量,这些变量分别持有了数组中对应元素的值。

所以当你执行这些Action时,每个Lambda都会输出各自捕获的那个str的取值,也就是abc def ghi。

如果是C# 4及更早的版本,foreach的行为和for循环类似,所有Lambda会捕获同一个变量,最终会输出三次ghi——这也是你最初预期其中一种结果的原因,但现在的C#版本已经修复了这个反直觉的设计。


内容的提问来源于stack exchange,提问作者Behold The Light

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:08:12