优雅的方式来冻结封闭

时间:2014-04-25 14:56:42

标签: c# closures

有没有优雅的方式来"冻结"从方法返回的动作中使用的变量?

看看下面的代码:

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);
}

有更好的方法吗?

3 个答案:

答案 0 :(得分:5)

没有通用方法 - 但如果您只是被foreach特定问题困扰,那么有两种选择:< / p>

  • 开始使用C#5编译器(您不需要以.NET 4.5或类似的方式为目标)。 The rules around captured 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;
}