关于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试试看。。
[解决办法]
[解决办法]
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#4.0的一个bug.
[解决办法]
问一下,从哪里知道foreach的实现是这个样子的啊
[解决办法]
学习了。。挺不错的,研究很快
[解决办法]
很容易出错啊,还不容易看出来。。。
[解决办法]
[解决办法]
坑爹啊,上次用foreach死不行,果断改成for后好了。。估计是这个原因。。
[解决办法]
终于找到原因了,太感谢楼主鸟,机动啊。。
[解决办法]
这样会养成不良的编程习惯
[解决办法]
[解决办法]
[解决办法]
- 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?
[解决办法]
[解决办法]
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 会自动提示的
[解决办法]
这样的问题,没研究过的真的容易掉进去啊
[解决办法]
很好 学习下