关于foreach语句在C#4.5中的改进,该如何处理
关于foreach语句在C#4.5中的改进
看这样一段代码,你觉得会输出什么呢?
如果你使用的是C# 4.0,运行结果是55555。
不要感到吃惊,因为在 C# 4.0 中,foreach的实现是这样的:
注意迭代变量x是在循环块外部被定义的。
这里涉及到一个很重要的概念,闭包,在Lambda表达式中,我们使用了外层的自由变量x,注意,在调用lambda表达式的时候,x会被求值,而这个定义在外部的x变量在循环终了等于5,这是为什么都是输出5的原因。
但是对于大多数程序员,他们希望的输出是12345,我们把上面的代码修改下:
这一次,我们将x定义到块的内部。因此每当循环执行一次,都会产生一个局部变量x,闭包就会对每一个迭代单独求值,所以输出就是我们期望的12345了。
因为这个问题,在C# 4.0时代,我们必须非常小心foreach对闭包的影响,在C# 4.5(VS11 Beta)中,编译器终于做出了改变。
回到开头的代码,在VS11 Beta中会产生12345的输出了。
最后说一下,如果你希望编写出C# 4.0和C# 4.5编译完全一致的代码,你可以这么写:
------解决方案--------------------
我猜是12345
------解决方案--------------------
研究的很细致!
------解决方案--------------------
我了解了,
------解决方案--------------------
还没注意到4.5有这改进,很好。
闭包是个坑爹的陷阱
------解决方案--------------------
明天去公司用vs11试试看。。
------解决方案--------------------
------解决方案--------------------
Closure is very useful in JavsScript, but for C#... I've never met any situation where I use it.
------解决方案--------------------
这样会养成不良的编程习惯
从概念上理解(() => x);将编译成一个方法,而x的内存地址被固定了。
foreach (var foo in actions)里面应该始终会被调用成一个返回值。
在其他语言里面,都是这样的,所以不应该这么写。
------解决方案--------------------
认真撸过
------解决方案--------------------
vs11里目标选4或者4.5,运行结果都是12345
------解决方案--------------------
js特有的吧?int型不是引用,理论上应该是12345才对,没试过,以后试试
------解决方案--------------------
看这样一段代码,你觉得会输出什么呢?
- C# code
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); foreach (int x in data) { actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
如果你使用的是C# 4.0,运行结果是55555。
不要感到吃惊,因为在 C# 4.0 中,foreach的实现是这样的:
- C# code
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); IEnumerator e = data.GetEnumerator(); int x = 0; while (e.MoveNext()) { x = (int)e.Current; actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
注意迭代变量x是在循环块外部被定义的。
这里涉及到一个很重要的概念,闭包,在Lambda表达式中,我们使用了外层的自由变量x,注意,在调用lambda表达式的时候,x会被求值,而这个定义在外部的x变量在循环终了等于5,这是为什么都是输出5的原因。
但是对于大多数程序员,他们希望的输出是12345,我们把上面的代码修改下:
- C# code
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); IEnumerator e = data.GetEnumerator(); while (e.MoveNext()) { int x = 0; x = (int)e.Current; actions.Add(() => x); } foreach (var foo in actions) { Console.WriteLine(foo()); }
这一次,我们将x定义到块的内部。因此每当循环执行一次,都会产生一个局部变量x,闭包就会对每一个迭代单独求值,所以输出就是我们期望的12345了。
因为这个问题,在C# 4.0时代,我们必须非常小心foreach对闭包的影响,在C# 4.5(VS11 Beta)中,编译器终于做出了改变。
回到开头的代码,在VS11 Beta中会产生12345的输出了。
最后说一下,如果你希望编写出C# 4.0和C# 4.5编译完全一致的代码,你可以这么写:
- C# code
int[] data = new int[] { 1, 2, 3, 4, 5 }; List<Func<int>> actions = new List<Func<int>>(); foreach (int x in data) { int x1 = x; actions.Add(() => x1); } foreach (var foo in actions) { Console.WriteLine(foo()); }
------解决方案--------------------
我猜是12345
------解决方案--------------------
研究的很细致!
------解决方案--------------------
我了解了,
------解决方案--------------------
还没注意到4.5有这改进,很好。
闭包是个坑爹的陷阱
------解决方案--------------------
明天去公司用vs11试试看。。
------解决方案--------------------
------解决方案--------------------
Closure is very useful in JavsScript, but for C#... I've never met any situation where I use it.
------解决方案--------------------
这样会养成不良的编程习惯
从概念上理解(() => x);将编译成一个方法,而x的内存地址被固定了。
foreach (var foo in actions)里面应该始终会被调用成一个返回值。
在其他语言里面,都是这样的,所以不应该这么写。
------解决方案--------------------
认真撸过
------解决方案--------------------
vs11里目标选4或者4.5,运行结果都是12345
------解决方案--------------------
js特有的吧?int型不是引用,理论上应该是12345才对,没试过,以后试试
------解决方案--------------------