使用Visual Studio 2013,我正在尝试重现Eric Lippert的博客文章"关闭循环变量被认为有害"中提到的问题.
在项目属性中,我选择"C#3.0"作为语言版本(Build> Advanced ...).此外,我选择".NET Framework 3.5"作为目标框架,好像我认为这不应该是必要的,因为这只涉及语言.
运行他的代码:
using System; using System.Collections.Generic; namespace Project1 { class Class1 { public static void Main(string[] args) { var values = new List() { 100, 110, 120 }; var funcs = new List >(); foreach (var v in values) { funcs.Add(() => v); } foreach (var f in funcs) Console.WriteLine(f()); } } }
预期产量:
120 120 120
实际产量:
100 110 120
正如埃里克·利珀特(Eric Lippert)自己所说的那样"C#在foreach中重用变量是否有原因?" :
该
for
循环将不会改变,并且改变将不会被"向后移植"到C#的早期版本.因此,在使用这个习语时你应该继续小心.
我究竟做错了什么?
斯科特的答案是正确的,但可以使用一些额外的澄清.
这里的问题是"语言版本"开关没有按照你的想法做.在我看来,这是一种错误,因为它有很多误导性."语言版本"开关并不意味着"使用旧编译器"; 它不是兼容模式.
相反,它意味着"使用当前编译器,如果我使用所选语言版本中没有的功能,则会产生错误."
这种转换的原因是开发团队中的一个人可以"试用"新版本的编译器以确保他们的代码仍然有效,但在他们签入之前就知道他们没有意外地使用了语言功能他们的队友的编译器会窒息.因此,如果您将语言版本设置为3.0,那么"dynamic"将无效(因为它是在C#4.0中添加的),但它仍然是您安装的任何版本的编译器.
正如Scott指出的那样,如果你想使用旧的编译器,你必须在你的机器上找到旧编译器的副本并明确地使用它.
有关此开关执行和不执行操作的更多示例,请参见http://ericlippert.com/2013/04/04/what-does-the-langversion-switch-do/.
我相信即使您为您的语言选择C#3.0,编译器仍然会执行新行为,我认为您不能在新编译器中重新创建旧行为.设置语言的唯一方法是限制您使用语言的更高版本中引入的语言功能.
您必须找到旧编译器(VS 2012或更早版本)的副本才能获得该行为.当Eric说"改变不会"被"移植"到以前版本的C#时,他的意思是他们不会发布VS2012的更新来改变该编译器中的行为(当你引用的帖子写成VS2013刚刚问世时和VS2012仍然广泛使用).
编辑:如果你真的想重新创建行为,你需要为每个循环编写自己的手册.
public static void Main(string[] args) { var values = new List() { 100, 110, 120 }; var funcs = new List >(); using (var enumerator = values.GetEnumerator()) { int v; while (enumerator.MoveNext()) { //int v; // In C# 5+ the variable is declared here. v = enumerator.Current; funcs.Add(() => v); } } foreach (var f in funcs) Console.WriteLine(f()); }
这将输出您的预期输出.