0

我知道很多关于 C# 闭包的问题已经提出、回答或讨论过。但是请在我的小实验上花点时间......

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var timer = new Timer(500))
      {
        timer.AutoReset = false;

        GetFunc2(timer, 0);
        // GetFunc3(timer, 0);

        timer.Start();

        Console.ReadLine();
      }
    }

    static void GetFunc2(Timer timer, int i)
    {
      for (; i < 5; ++i)
      {
        timer.Elapsed += (obj, e) =>
          {
            Console.WriteLine(i);
          };
      }
    }

    static void GetFunc3(Timer timer, int i)
    {
      timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i++);
      };

      timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i++);
      };

      timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i++);
      };

      timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i++);
      };

      timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i++);
      };
    }
  }
}

通过单独调用GetFunc2GetFunc3Main我们可以看到输出是不同的,虽然GetFun3看起来只是 . 的简单扩展GetFunc2。有谁知道为什么?我认为 ildasm 可以揭示不同的生成代码,但我确实想知道为什么。在 VS2012 Pro、.net 4.5 上测试。

4

3 回答 3

3

这被覆盖的次数是压倒性的。

我将不再讨论讨论,但请看 Eric Lippert 的回答和其他人:

https://stackoverflow.com/a/8899347/1517578

对这些答案的评论也很有趣。

此外,Eric 的博客文章的链接在这里:http: //blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

基本上,您需要将关闭的变量复制到局部变量中:

static void GetFunc2(Timer timer, int i)
{
  for (; i < 5; ++i)
  {
    int i2 = i; // STORE IT
    timer.Elapsed += (obj, e) =>
      {
        Console.WriteLine(i2); // USE THE NEWLY STORED VERSION
      };
  }
}

这导致:

0
1
2
3
4

..正如预期的那样。

于 2013-02-18T00:17:01.327 回答
0

如果GetFunc2你在调用(执行)匿名方法之前递增i,因此当你调用你看到的方法时,它i已经递增了。

如果GetFunc3您在调用每个匿名方法之前不递增i,因此它从零开始并随着每次调用而不断递增。

于 2013-02-18T00:29:16.860 回答
0

AFAIK 闭包始终包含对“父”方法的局部变量的引用。所以不同之处在于您在实际调用 GetFunc2 中的这些函数之前增加了值。所以在调用的时候,已经有 5 的值了。在 GetFunc3 中,您在引发事件时递增该值,因此它会计数。

于 2013-02-18T00:23:50.457 回答