Elegant way to freeze closures

老子叫甜甜 提交于 2021-02-08 08:07:35

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!