首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

关于foreach语句在C#4.5中的改进,该如何处理

2012-04-20 
关于foreach语句在C#4.5中的改进看这样一段代码,你觉得会输出什么呢?C# codeint[] data new int[] { 1,

关于foreach语句在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){    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试试看。。
[解决办法]
探讨
明天去公司用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才对,没试过,以后试试
[解决办法]
探讨
这充分说明foreach是C#语法糖,和CLR完全无关。



[解决办法]
这个根本就是C#4.0的一个bug.
[解决办法]
问一下,从哪里知道foreach的实现是这个样子的啊
[解决办法]
学习了。。挺不错的,研究很快
[解决办法]
很容易出错啊,还不容易看出来。。。
[解决办法]
探讨

认真撸过

[解决办法]
坑爹啊,上次用foreach死不行,果断改成for后好了。。估计是这个原因。。
[解决办法]
终于找到原因了,太感谢楼主鸟,机动啊。。
[解决办法]
这样会养成不良的编程习惯


[解决办法]
探讨

这样会养成不良的编程习惯

[解决办法]
探讨

引用:
vs11里目标选4或者4.5,运行结果都是12345

这充分说明foreach是C#语法糖,和CLR完全无关。

[解决办法]
C# code
var data = new int[] { 1, 2, 3, 4, 5 };var actions = data.Select(x1 => (Func<int>) (() => x1)).ToList();foreach (var foo in actions){    Console.WriteLine(foo());}
[解决办法]
呵呵,不过和 foreach 没有关系了~~~
[解决办法]
这个好像很类似javascript的问题:


var result = [];
for(var i=0; i < 10; i++) {
result.push(function () { return i })
}
console.log(result[5]()); // 10, not 5

解决方法是使用闭包:

var result = [];
for(var i=0; i < 10; i++) {
(function (i) { // copied i
result.push(function () { return i })
}(i)); // original i
}
console.log(result[5]()); // 5

[解决办法]
http://sd.csdn.net/a/20120326/313536.html
[解决办法]
探讨
研究的很细致!

[解决办法]
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#里面的闭包,学习了。
[解决办法]
就只是一个遍历,过度包装的如些复杂
[解决办法]
这个在2.0下面也是这样的,55555
[解决办法]
65楼解释的不错
[解决办法]
C# 4.5


还是.net framework 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)
{
actions.Add(() => x);
}
foreach (var foo in actions)
{
Console.W……


[解决办法]
v研究的很细致!非常好
[解决办法]
不觉得这个新功能有什么特别好的地方啊~~楼主能解释下
[解决办法]
不是什么改进,不如原来好理解
[解决办法]
public delegate void Go();

public class B {

public Go mydel;


public void Write() {
this.mydel();
}

}

第一个

static void Main(string[] args)
{

B b = new B();
for (int i=0; i < 7; i++)
{
int value = i;
b.mydel += delegate { Console.WriteLine("hello world" + value); };
}
b.Write();
}


第二个

static void Main(string[] args)
{

B b = new B();
for (int i=0; i < 7; i++)
{

b.mydel += delegate { Console.WriteLine("hello world" + i); };
}
b.Write();
}

觉得这两种情况 输出的会一样吗?
[解决办法]
还有这样奇怪的事
[解决办法]
大千世界无奇不有,一个标点还能搞死一个项目呢。
[解决办法]
lamba = 匿名函数 = 方法

委托 真正被执行时 是这个事件被触发了。


这 就是一个 延迟执行 没什么
[解决办法]
认真读读,好像是有这么回事,学习了
[解决办法]
用 Resharper 会自动提示的
[解决办法]
这样的问题,没研究过的真的容易掉进去啊
[解决办法]
很好 学习下

热点排行