.Net匿名方法......奇迹在哪里?

时间:2009-08-16 06:47:25

标签: .net lambda

这个函数返回1210而不是385,为什么?

public int CalcSquaresSum() {
    int sumOfSquares = 0;
    List<Func<int>> functions = new List<Func<int>>();
    for (int i = 1; i <= 10; i++) {
        functions.Add(() => i * i);
    }

    foreach (var function in functions) {
        sumOfSquares += function(); // why function() is always 121
    }

    return sumOfSquares;
}

6 个答案:

答案 0 :(得分:7)

在你的代码中,循环变量i对所有函数都是通用的,它将留给它的最后一个值11,稍后将用它来计算总和。

如果更改循环以将变量分配给未共享的范围中的函数,就像这样......

  for(int i = 1;i <= 10;i++) {
    int n = i;
    functions.Add(() => n * n);
  }

...该函数将返回385。

答案 1 :(得分:5)

因为i的值在循环结束时为11。你已经在列表中添加了一堆函数 - 而不是值 - 但是函数都指向一个int,显然只能有一个值。它在你的陈述开始时被宣布过一次。与任何变量一样,它的值将是你对它做的最后一件事。当这些函数实际运行时,它们都会针对该值运行。

如果在循环中声明一个新变量,那个变量永远不会改变(永远不会被重新赋值),所以当函数运行时,它仍然会引用一个未触及的值。

答案 2 :(得分:2)

您正在引用变异变量,而不是捕获值。

其他答案显示了如何执行此操作。

答案 3 :(得分:0)

我只是在你调用function()之后才进行评估 那一刻我== 11

很有趣,因为我希望我不再可用..:D

答案 4 :(得分:0)

通过在第一个循环中添加委托列表,您创建了所谓的closure。实际上,范围显然限制在循环中的局部变量现在存储为嵌套类的成员(由编译器生成)。这就是为什么当你执行第二个循环时,使用循环变量的闭包版本。因为它只在第一个循环完成后才使用,所以所有“i的值”现在都是11。

因此,结果将是:10 *(11 * 11)= 1210。

答案 5 :(得分:0)

为了好奇,这是编译器在编写代码时实际生成的内容:

[System.Runtime.CompilerServices.CompilerGenerated]
private sealed class AnomClass
{
    public int i;

    public int CalcSquaresSum_AnonFunc() {
        return (this.i * this.i);
    }
}

public int CalcSquaresSum()
{
    int sumOfSquares = 0;
    List<Func<int>> functions = new List<Func<int>>();

    AnomClass anonObj = new AnomClass();
    Func<int> anonFunc = null;

    for (anonObj.i = 1; anonObj.i <= 10; anonObj.i++) {
        if (anonFunc == null)
            anonFunc = new Func<int>(anonObj.CalcSquaresSum_AnonFunc);
        functions.Add(anonFunc);
    }

    foreach (Func<int> function in functions) {
        sumOfSquares += function();
    }
    return sumOfSquares;
}

正如你所看到的,没有任何魔法涉及。匿名方法只能访问在其作用域之外声明的变量,因为它实际上并未在其作用域之外声明。

真正发生的是变量i移动到一个不可见的类。匿名方法驻留在该类上。从那里它可以直接访问i。在CalcSquaresSum方法中,对i的引用都被翻译为对不可见类的引用。

请注意,变量sumOfSquares接受相同的处理。当然,这是因为编译器足够聪明,意识到匿名方法只使用了i