问题
Is there any elegant way to "freeze" the variables used in an action that is returned from a method?
Just have a look at the following code:
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;
}
This code will write two lines to the console, both containing "b". The reason for this is not too hard to find: The last value for "s" in ClosureTrap is "b".
Is there any elegant way to get two lines "a" and "b" as output on the console?
Currently I am using an additional method to create the delegate. But by doing this, the closures loose a lot of their elegance:
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);
}
Is there a better way to do this?
回答1:
There's no general way to do this - but if you're only bothered by the specific issue of foreach
, then there are two options:
- Start using a C# 5 compiler (you don't need to target .NET 4.5 or anything like that). The rules around captured foreach iteration variables changed (to sane ones) for C# 5.
If you're stuck with C# 3 or 4, capture a local variable declared in the loop:
foreach(String s in strings) { string copy = s; result.Add(() => Console.WriteLine(copy)); }
That way each iteration of the loop captures a separate variable, which never changes value.
回答2:
foreach(String s in strings)
{
var localCopy = s; // <= local copy
result.Add(() => Console.WriteLine(localCopy));
}
You need to make a local copy of your variable.
回答3:
Well this seems to work:
static List<Action> ClosureTrap(params String[] strings)
{
List<Action> result = new List<Action>();
strings.ToList().ForEach(s => result.Add(() => Console.WriteLine(s)));
return result;
}
来源:https://stackoverflow.com/questions/23296532/elegant-way-to-freeze-closures