揭开lambda如何运作的神秘面纱

时间:2012-07-14 14:39:39

标签: c# lambda

  

可能重复:
  Detailed Explanation of Variable Capture in Closures

public class Polynom {
    public delegate double Func(double x);
    private Func f;
    public Polynom(params double[] coef) {
        f = (double x) => {
        double sum = 0;
        for ( int i = 0 ; i < coef.Length ; i++ )
            sum += coef[i] * Math.Pow(x,coef.Length-1-i);
        return sum;
        };
    }
    public double evaluate(double x) {
        return f(x);
    }
    public static void Main() {
        Polynom a=new Polynom(1,1,1);
        Polynom b=new Polynom(2 , 2 , 0);
        Console.WriteLine(a.evaluate(2));
        Console.WriteLine(b.evaluate(2));
        Console.ReadKey();
    }
}

注意f中的代码如何使用coef,而coef是构造函数的参数。 如果你考虑一下,除非获得coef的ref副本,否则它应该不起作用,因为一旦构造函数完成它的工作,它的参数就会消失。但不知何故,调用f设法使用coef,就像它仍然存在一样。怎么样?

如果有人能够解释这一点,我会非常喜欢这个......

我想知道的另一件事是,每个Polynom实例上的代码都是相同的,但是每个实例都会获得相同代码的另一个副本吗?如果是这样,有没有办法让我的课程只运行该代码的一个副本? (比如让它以某种方式静止)

2 个答案:

答案 0 :(得分:2)

该函数是一个所谓的闭包,在this wikipedia article

中有详细说明
  

闭包允许函数访问其直接词法范围之外的变量。 upvalue是一个自由变量,它已被一个闭包绑定(关闭)。关闭据说“关闭”其上涨价值。引用环境在创建闭包时将非局部名称绑定到范围内的相应变量,另外将其生命周期延长至至少与闭包本身的生命周期一样长。当稍后输入闭包时,可能来自不同的范围,执行该函数时,其非局部变量引用闭包捕获的那些变量。

关于你的第二个问题:使闭包静止有点与功能原则的目的相矛盾。

答案 1 :(得分:2)

Lambdas和其他委托实现为closures,由编译器创建的特殊对象将lambda的方法与lambda完成其执行所需的所有数据相结合。 lambda中使用的所有局部变量和参数的值都被隐式捕获为闭包的数据成员,因此它们在lambda本身不再被引用之前一直可用。

您可以将闭包视为专门为您的lambda创建的特殊匿名类。在您的情况下,闭包可能如下所示:

private Polynom_Closure {
    private readonly double[] coef;
    public Polynom_Closure(double[] coef) {
        this.coef = coef;
    }
    public double evaluate(double x) {
        double sum = 0;
        for ( int i = 0 ; i < coef.Length ; i++ )
            sum += coef[i] * Math.Pow(x,coef.Length-1-i);
        return sum;
    }
}

编译器使这个类不可见,然后将其用于代码:

public Polynom(params double[] coef) {
    f = new Polynom_Closure(coef).evaluate;
}