为什么第二个写字线在使用lambda时打印出12?

时间:2015-01-22 15:10:29

标签: c# lambda functional-programming

下面的代码

    int factor = 2;
    Transformer sqr = x => x * factor;
    Console.WriteLine(sqr(3)); // 6
    factor = 4;
    Console.WriteLine(sqr(3)); // 12

我认为lambda应该在编译时捕获因子,因此两个writeline的结果应该是相同的。

但是,当我运行时,我得到6和12,C#lambda也使用动态范围? 我认为lambda应该使用一种叫做“词法范围”的东西

3 个答案:

答案 0 :(得分:4)

首先,您无法在编译时捕获任何内容。 lambda的环境只能在运行时捕获,因为这是它存在的唯一时间。

当一个变量被lambda捕获时,lambda实际上可以在整个生命周期内直接访问该变量; lambda(google“访问修改后的闭包”)可以看到外部值的变化,反之亦然。

如果你想要将lambda与外部干扰隔离开来,你必须使它捕获“外部”中没有人可以访问的东西 - 这意味着一个范围有变量的变量已经足够有限了。

考虑:

int factor = 2;
Func<int, Func<int, int>> generateTransformer = f => x => x * f;
Func<int, int> sqr = generateTransformer(factor);

Console.WriteLine(sqr(3)); // 6
factor = 4;
Console.WriteLine(sqr(3)); // 6

这里发生的是sqr是调用generateTransformer的结果,它正在f的主体内捕获局部变量generateTransformer的值。这个价值永远不会被任何人改变,因为generateTransformer的身体以外的任何人都看不到它。致电generateTransformer会复制factor的当前值,并将该“冻结”副本提供给sqr以供使用。

答案 1 :(得分:2)

捕获factor。它将它捕获为隐藏闭包类的成员变量,以及lambda的方法。你最终会得到与

大致类似的东西
private class sqrClosure
{
    public int factor;
    public int srq(int x)
    {
        return x * factor;
    }
}

...

var c = new sqrClosure();
c.factor = 2;
Console.Writeline(c.sqr(3));
c.factor = 4;
Console.Writeline(c.sqr(3));

答案 2 :(得分:1)

它捕获factor,但它捕获变量而不是变量的

因此,当您更改变量,然后调用lambda时,它将获取捕获变量的当前值。