C#中代理问题

在下面的程序中,DummyMethod总是打印5.但是如果我们使用注释代码,我们会得到不同的值(即1,2,3,4)。 任何人都可以解释为什么会这样吗?

delegate int Methodx(object obj); static int DummyMethod(int i) { Console.WriteLine("In DummyMethod method i = " + i); return i + 10; } static void Main(string[] args) { List methods = new List(); for (int i = 0; i < 5; ++i) { methods.Add(delegate(object obj) { return DummyMethod(i); }); } //methods.Add(delegate(object obj) { return DummyMethod(1); }); //methods.Add(delegate(object obj) { return DummyMethod(2); }); //methods.Add(delegate(object obj) { return DummyMethod(3); }); //methods.Add(delegate(object obj) { return DummyMethod(4); }); foreach (var method in methods) { int c = method(null); Console.WriteLine("In main method c = " + c); } } 

此外,如果使用以下代码,我会得到所需的结果。

  for (int i = 0; i < 5; ++i) { int j = i; methods.Add(delegate(object obj) { return DummyMethod(j); }); } 

问题是你在每个委托中捕获相同的变量i – 在循环结束时只有值5。

相反,您希望每个委托捕获一个不同的变量,这意味着在循环中声明一个新变量:

 for (int i = 0; i < 5; ++i) { int localCopy = i; methods.Add(delegate(object obj) { return DummyMethod(localCopy); }); } 

这是一个非常常见的“问题” - 您可以在我的闭包文章中阅读更多有关捕获的变量和闭包的内容 。

本文可能会帮助您了解正在发生的事情(即闭包是什么): http : //blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx

如果查看生成的代码(使用Reflector),您可以看到差异:

 private static void Method2() { List list = new List(); Methodx item = null; <>c__DisplayClassa classa = new <>c__DisplayClassa(); classa.i = 0; while (classa.i < 5) { if (item == null) { item = new Methodx(classa.b__8); } list.Add(item); classa.i++; } foreach (Methodx methodx2 in list) { Console.WriteLine("In main method c = " + methodx2(null)); } } 

当你使用初始代码它在后台创建一个临时类时,这个类保存对“i”变量的引用,因此根据Jon的答案,你只能看到它的最终值。

 private sealed class <>c__DisplayClassa { // Fields public int i; // Methods public <>c__DisplayClassa(); public int b__8(object obj); } 

我真的建议查看Reflector中的代码以查看正在发生的事情,以及我如何理解捕获的变量。 确保在Option菜单中将代码优化设置为“.NET 1.0”,否则它将隐藏所有幕后的东西。

我认为这是因为变量i被放入堆中(它是一个捕获的变量

看看这个答案 。