C#中for循环和foreach循环

比较以下两段代码:

Console.WriteLine("Hello,");
List<string> pets = new List<string>() { "Tom", "Jerry", "Spike" };
List<Action> callings = new List<Action>();
foreach (string pet in pets)
{
    callings.Add(()=>Console.WriteLine(pet));
}
foreach (Action calling in callings)
{
    calling();
}

Console.WriteLine("Hello,");
List<string> pets = new() { "Tom", "Jerry", "Spike" };
List<Action> callings = new();
for (int i = 0; i < pets.Count; i++)
{
    callings.Add(() => Console.WriteLine(pets[i]));
}
foreach (Action calling in callings)
{
    calling();
}

预计这两段打印结果会是什么呢?先看第一段代码的执行结果:

结果和预期是一样的:

foreach循环中,匿名函数捕获变量的当前值。当委托被调用时,传入的都是委托被创建时该变量的值。

再看第二段代码执行结果:抛出“System.ArgumentOutOfRangeException“报错,设置断点观察发现是foreach第一次循环执行委托时报的错,此时匿名委托集合的第一个委托中的打印元素为pets[3],超出pets集合的最大索引范围。为了更直观的观测匿名委托集合的参数,将pets的索引减去1后传给匿名委托打印:

Console.WriteLine("Hello,");
List<string> pets = new() { "Tom", "Jerry", "Spike" };
List<Action> callings = new();
for (int i = 0; i < pets.Count; i++)
{
    callings.Add(() => Console.WriteLine(pets[i - 1]));
}
foreach (Action action in callings)
{
    action();
}

运行结果:

结果是不是超预期了?

在for循环中,匿名函数将pets[i - 1]当作打印结果,每次迭代后委托打印结果都是:

 Console.WriteLine(pets[i - 1]);

而不是

Console.WriteLine(pets[0]);
Console.WriteLine(pets[1]);
...
Console.WriteLine(pets[i]);

所以for循环后,i被迭代为3,而匿名委托的最终内容:

Console.WriteLine(pets[2]); 
//此时i已经迭代为3了,如果在for循环中创建打印pets[i]委托,在执行该委托时会超出索引而报错

标注:如果使用C#5.0之前的版本可能结果会不一样,但是在这之后就不用担心结果差异了。

委托在被创建后没有立即执行,打印对象被当作变量保存了下来,变量i被迭代时,所有委托中的打印对象都一起被迭代,最终所有委托被执行时打印结果全部相同了。如果在for循环中迭代变量就被消费了,则不会出现这种结果:

Console.WriteLine("Hello,");
List<string> pets = new() { "Tom", "Jerry", "Spike" };
for (int i = 0; i < pets.Count; i++)
{
    Console.WriteLine(pets[i]);
}

运行结果:

这时候结果就与预期一致了。而在foreach循环中,每次迭代都为匿名委托引入新的变量,循环迭代时并不影响之前被捕获的变量。

for循环和foreach循环的显著差别是:在C#5.0及以后的版本中,foreach的每次循环都会引入新的变量,而for循环每次都是捕获同一个变量。

其实在这背后,是匿名方法在作祟。匿名方法会捕获迭代变量,并影响了迭代变量的生命周期。在C#5之前,foreach循环也存在这种问题,但是在C#5中这个情况被设计团队修正了。所以你可能会感觉在for循环中只有一个循环变量,而foreach中每次迭代都声明了一个新的变量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穷的捡破烂儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值