Has foreach's use of variables been changed in C# 5?
在这个答案中,https://stackoverflow.com/a/8649429/1497-eric lippert说,"仅供参考,我们很可能在下一个版本的c中解决这个问题,这是开发人员在foreach循环如何使用变量方面的一个主要难点"。
In the next version each time you run through the"foreach" loop we will generate a new loop variable rather than closing over the same variable every time. This is a"breaking" change but in the vast majority of cases the"break" will be fixing rather than causing bugs.
我还没有找到任何迹象表明这一变化已经发生。是否有任何迹象表明这是foreach循环在C 5中的工作方式?
- 所指的foreach是C语言功能,而不是.NET框架功能。
- 谢谢@boltclock。我已将问题更新为目标C 5。不过,为了澄清这一点,如果我在与2012年的对比中以.NET 4.5为目标构建一个C项目,我是否有可能使用C 5?
- @A诳hardin:无论目标框架是什么,在VS2012中的任何项目都将使用C诳5.0,除非您要求它使用不同的编译器。若要将编译器更改为特定版本,请转到"项目属性"、"生成"选项卡,然后单击"高级"…在右下角。然后更改"语言版本"下拉列表。
- @Jeffyates我尝试在VS2012中更改语言版本,但它一直遵循"新"约定(即,foreach变量是一个"内部"变量,在每次迭代中捕获不同的"副本")。你真的验证过改变语言版本对你有用吗?(这主要是理论上的兴趣;我认为没有人会希望C 4行为在实践中出现)。
- @杰佩斯蒂格尼尔森:有趣。我没试过。也许它只考虑语法差异,而不考虑规范的更改。真令人惊讶。有机会的时候我得去看看。你怎么证实的?IL或应用程序行为?
- @杰弗里茨的行为。为此,我有一个小的控制台应用程序(与您回答的第一条评论中的应用程序没什么不同)。也许我在重新编译时做了一些错误,所以如果有人试图复制/验证,那就更好了。
- 将langversion更改为早于c_5的版本仍将使用新的c_5约定进行编译。langversion只检查语法是否与C版本兼容,它不会更改已编译代码的行为。见github.com/dotnet/roslyn/issues/8034
这是对C语言的更改,而不是.NET框架。因此,它只影响在C_5.0下编译的代码,而不管该代码将在哪个.NET框架版本上执行。
C 5
本规范第8.8.4节明确说明已进行了此项变更。具体来说,C 5.0规范第249页规定:
1
| foreach (V v in x) embedded-statement |
is then expanded to:
1 2 3 4 5 6 7 8 9 10 11 12
| {
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
} |
后来:
The placement of v inside the while loop is important for how it is
captured by any anonymous function occurring in the
embedded-statement.
C 4
当与C 4.0规范进行比较时,对规范的更改是明确的,C 4.0规范规定(同样,在第8.8.4节中,但这次,第247页):
1
| foreach (V v in x) embedded-statement |
is then expanded to:
1 2 3 4 5 6 7 8 9 10 11 12 13
| {
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
} |
注意变量v是在循环外部而不是在循环内部声明的,就像C 5.0那样。
注释
您可以在VC#\Specifications\1033下的Visual Studio安装文件夹中找到C规范。这是VS2005、VS2008、VS2010和VS2012的情况,使您可以访问C 1.2、2.0、3.0、4.0和5.0的规范。您也可以通过搜索C# Specification在msdn上找到规格。
- 一个很好的例子是:List list = new List(); foreach (var i in new int[] { 1, 2, 3, 4, 5 }) { list.Add(() => Console.WriteLine(i)); } foreach (var f in list) { f(); }。
- 我不确定这里是否需要一个例子,因为操作人员已经了解正在讨论的问题,并且只是在寻找变更的确认。
- 也就是说,谢谢你寄了一张。我可以从中看到这个问题的未来观众需要上下文的价值。也许它可以在某种程度上被编辑,而不会造成混乱。谢谢。
- 这是否会影响性能,因为必须为每个迭代创建一个新的变量?
- @Erwinmayer:可能,我不知道没有分析,但是我怀疑这是可以忽略的,并且在使用闭包时不需要这样做的好处很可能超过任何轻微的性能损害。
- 为什么早期的C规范说类似于C 5.0 foreachmsdn.microsoft.com/en-gb/library/aa664754.aspx
- @科林方:我不知道。我检查了C 1.2规范(csharpindepth.com/articles/chapter1/specifications.aspx),您是正确的。它以前和现在一样。然而,这是前仿制药,我猜C 2.0已经有了改变。
- @科林方:似乎是C 3.0的变化。有趣的是,这个变化是在那个时候发生的。我不知道为什么。
- @Colinfang:根据Eric Lippet的评论,C 4之前的规范是错误的(stackoverflow.com/questions/8649337/…)