提问人:Denis Sivtsov 提问时间:2/7/2023 最后编辑:Denis Sivtsov 更新时间:2/13/2023 访问量:577
闭包 Lambda 在 for 和 foreach c 中的特定性#
Specific of Closure Lambda in for and foreach c#
问:
我阅读了有关此主题的不同文章 Eric Lippert 的博客和其他 这里 并且知道为什么这两个代码的工作方式不同
int[] values = { 7, 9, 13 };
List<Action> f = new List<Action>();
foreach (var value in values)
f.Add( () => Console.WriteLine("foreach value: " + value));
foreach (var item in f)
item();
f = new List<Action>();
for (int i = 0; i < values.Length; i++)
f.Add(() => Console.WriteLine("for value: " + ((i < values.Length) ? values[i] : i)));
foreach (var item in f)
item();
但是我没有找到明确的解释为什么最终(从编译器 c# 版本 5 开始)决定“foreach 循环变量在逻辑上将位于循环主体内,因此闭包每次都会获得新的副本。 因为旧的实现提供了使用闭包 Lambda 的更多自由(“按值或按引用”),但要求程序员谨慎使用它,如果您需要 foreach 的当前实现,您应该使用以下代码:
foreach(var v in values)
{
var v2 = v;
funcs.Add( ()=>v2 );
}
问题:
问题1.我想知道为什么最后决定改变 foreach 的实现(我读到的利弊,这是 50/50 Eric Lippert 的博客)?
问题2.在“for”循环的情况下,它是一个超出循环操作范围的值(这会产生一个非常具体的情况,其中 lambda 得到一个你永远不会在循环中获得的值),为什么它“失控”,因为它是一个非常容易出错的情况?(问题 2 更具修辞性,因此可以跳过)
附加说明(针对 Q1) 了解选择此实现的原因会很有趣 - 为什么 C# 的开发人员从版本 5 开始更改了 foreach 循环的闭包原则。或者为什么他们为 foreach 循环做了,但没有为 for 做。
答:
我想知道为什么最后决定改变
foreach
因为 (1) 用户坚信编译器的行为充其量是出乎意料的,最坏的情况是完全错误的,并且 (2) 没有令人信服的理由来保留奇怪的行为。导致设计团队不进行重大更改的最大因素是“因为真正的代码依赖于当前的行为”,但依赖于该行为的真实代码可能是错误的!
这是一个相当容易做出的决定。很多人抱怨这种行为;根本没有人抱怨修复。这是一个很好的电话。
为什么他们为 The Loop 做了,但没有为 .
foreach
for
(1)人们没有抱怨这个循环,(2)改变会困难得多,(3)改变更有可能产生现实世界的中断。for
人们可以合理地期望 a 的“循环变量”不是“实数”变量。你永远不会改变它;运行时会为您更改它:foreach
foreach(char c in "ABCDEFG")
{
c = 'X'; // This is illegal! You cannot treat c as a variable.
}
但对于循环的循环变量来说,情况并非如此;它们确实是变量。for
for(int i = 0; i < 10; i += 1)
i = 11; // weird but legal!
for
循环要复杂得多。您可以有多个变量,它们可以任意更改值,它们可以在循环外声明,等等。最好不要冒着破坏某人的风险,改变这些变量在循环中的处理方式。
评论
final