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

发布于 2024-08-01 22:43:12 字数 421 浏览 4 评论 0原文

这个函数返回 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;
}

This function returns 1210 but not 385, why ?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

GRAY°灰色天空 2024-08-08 22:43:12

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

如果您更改循环以将变量分配给不共享作用域中的函数,就像这样......

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

该函数将返回 385。

In your code the loop variable i is common to all functions and it will be left to it's last value, 11, which will be later used to calculate the sum.

If you change the loop to assign the variable to the functions in a scope that is not shared, like this...

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

...the function will return 385.

め七分饶幸 2024-08-08 22:43:12

因为循环结束时 i 的值为 11。 您已将一堆函数添加到列表中 - 不是值 - 但这些函数都指向一个 int,显然它只能有一个值。 它在 for 语句的开头声明过一次。 与任何变量一样,它的值将是您对它所做的最后一件事。 当这些函数实际运行时,它们都会针对该值运行。

如果您在循环内声明一个新变量,该变量将永远不会改变(永远不会被重新分配),因此当函数运行时,它仍然会引用一个未更改的值。

Because the value of i is 11 at the end of your loop. You've added a bunch of functions to the list - not values - but the functions all point to a single int, which obviously can only have one value. It was declared once, at the beginning of your for statement. As with any variable, its value will be the last thing you did to it. When those functions actually run, they all run against that one value.

If you declare a new variable inside the loop, that one will never change (never be reassigned) so when the function runs, it will still have a reference to an untouched value.

很快妥协 2024-08-08 22:43:12

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

其他答案向您展示了如何做到这一点。

You are referencing a mutating variable, and not capturing the value.

The other answers shows you how to do this.

原来是傀儡 2024-08-08 22:43:12

i*i 仅在调用 function() 后才会被求值..
那一刻我==11

不过很有趣,因为我希望我不再有空了..:D

i*i gets evaluated only just after you call function()..
At that moment i==11

Funny though because I'd expect i not to be available anymore.. :D

活雷疯 2024-08-08 22:43:12

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

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

By adding a list of delegates in the first loop, you've created what is known as a closure. Effectively, your local variable with scope apparently limited to the loop is now stored as a member of a nested class (generated by the compiler). This is why, when you execute the second loop, the closure-version of the loop variable is used. Since it's only used once the first loop has completed, all "values of i" are now 11.

So, the result will be: 10 * (11*11) = 1210.

羞稚 2024-08-08 22:43:12

出于好奇,以下是当您编写该代码时编译器实际生成的内容:

[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

For the sake of curiosity, here is what the compiler actually generates when you write that code:

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

As you can see, there's no magic involved. The anonymous method can only access a variable that is declared outside of its scope because it's not really declared outside of its scope.

What really happens is that the variable i s moved to an invisible class. The anonymous method resides on that class. From there it can access i directly. Inside the CalcSquaresSum method, the references to i are all translated as a reference to the invisible class.

Notice that the variable sumOfSquares receive the same treatment. Of course, that happens because the compiler is smart enough to realize that only i was used by the anonymous method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文