有没有优雅的方式来"冻结"从方法返回的动作中使用的变量?
看看下面的代码:
static void Main(String[] args)
{
foreach(Action a in ClosureTrap("a", "b"))
{
a();
}
}
static List<Action> ClosureTrap(params String[] strings)
{
List<Action> result = new List<Action>();
foreach(String s in strings)
{
result.Add(() => Console.WriteLine(s));
}
return result;
}
此代码将向控制台写入两行,两行包含&#34; b&#34;。 其原因并不难找到:&#34; s&#34;的最后一个值。在ClosureTrap中是&#34; b&#34;。
是否有任何优雅的方式来获得两条线&#34; a&#34;和&#34; b&#34;作为控制台上的输出?
目前我正在使用其他方法来创建委托。但通过这样做,封闭件失去了很多优雅:
static List<Action> ClosureTrap(params String[] strings)
{
List<Action> result = new List<Action>();
foreach(String s in strings)
{
result.Add(Freeze(s));
}
return result;
}
static Action Freeze(String s)
{
return () => Console.WriteLine(s);
}
有更好的方法吗?
答案 0 :(得分:5)
没有通用方法 - 但如果您只是被foreach
的特定问题困扰,那么有两种选择:< / p>
foreach
iteration variables changed (to sane ones) for C# 5. 如果您坚持使用C#3或4,请捕获循环中声明的局部变量 :
foreach(String s in strings)
{
string copy = s;
result.Add(() => Console.WriteLine(copy));
}
这样循环的每次迭代都会捕获一个单独的变量,该变量永远不会改变值。
答案 1 :(得分:1)
foreach(String s in strings)
{
var localCopy = s; // <= local copy
result.Add(() => Console.WriteLine(localCopy));
}
您需要制作变量的本地副本。
答案 2 :(得分:0)
这似乎有效:
static List<Action> ClosureTrap(params String[] strings)
{
List<Action> result = new List<Action>();
strings.ToList().ForEach(s => result.Add(() => Console.WriteLine(s)));
return result;
}