我知道使用匿名函数,本地堆栈变量被提升为一个类,现在在堆上等等.所以以下方法不起作用:
using System;
using System.Collections.Generic;
using System.Linq;
namespace AnonymousFuncTest
{
class Program
{
static void Main(string[] args)
{
foreach (var f in GetFuncs())
{
Console.WriteLine(f());
}
Console.ReadLine();
}
static IEnumerable<Func<int>> GetFuncs()
{
List<Func<int>> list = new List<Func<int>>();
foreach(var i in Enumerable.Range(1, 20))
{
list.Add(delegate() { return i; });
}
return list;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道将GetFuncs更改为可行的方法:
static IEnumerable<Func<int>> GetFuncs()
{
foreach(var i in Enumerable.Range(1, 20))
{
yield return () => i;
}
}
Run Code Online (Sandbox Code Playgroud)
但是说我做的事情如下:
foreach (var arg in someArgList)
{
var item = new ToolStripMenuItem(arg.ToString());
ritem.Click += delegate(object sender, EventArgs e)
{
new Form(arg).Show();
};
mainMenu.DropDownItems.Add(ritem);
}
Run Code Online (Sandbox Code Playgroud)
这当然没有预期的效果.我知道为什么它不起作用,只需要有关如何修复它的建议.
你应该改变它:
static IEnumerable<Func<int>> GetFuncs()
{
List<Func<int>> list = new List<Func<int>>();
foreach (var i in Enumerable.Range(1, 20))
{
int i_local = i;
list.Add(() => i_local);
}
return list;
}
Run Code Online (Sandbox Code Playgroud)
编辑
感谢Jon Skeet,请阅读他的回答.
只是详细说明kek444的答案,问题不在于捕获局部变量 - 而是所有委托都捕获了相同的局部变量.
在循环中使用变量的副本,在循环的每次迭代中"实例化"新变量,因此每个委托捕获不同的变量.有关详细信息,请参阅我关于闭包的文章.
另一种方法:
对于这种特殊情况,使用LINQ实际上是一个不错的选择:
static IEnumerable<Func<int>> GetFuncs()
{
return Enumerable.Range(1, 20)
.Select(x => (Func<int>)(() => x))
.ToList();
}
Run Code Online (Sandbox Code Playgroud)
如果你想进行懒惰的评估,你可以放弃ToList()通话.