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

と を個別に呼び出すGetFunc2GetFunc3、単に を単純に展開したように見えMainますが、出力が異なることがわかります。誰でも理由を知っていますか?ildasm は生成されたさまざまなコードを明らかにできると思いますが、その理由を知りたいです。VS2012 Pro、.net 4.5 でテスト済み。GetFun3GetFunc2

4

3 に答える 3

3

これがカバーされた回数は圧倒的です..

議論については再度説明しませんが、ここで Eric Lippert の回答と他の回答を見てください。

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

それらの回答に対するコメントも興味深い読み物です。

また、それに関する Eric のブログ投稿へのリンクは次のとおりです

基本的に、クローズされた変数をローカル変数にコピーする必要があります。

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インクリメントする場合、つまり、表示されているメソッドを呼び出すと、すでにインクリメントされています。ii

各匿名メソッドが呼び出されるまでGetFunc3インクリメントしない場合は、ゼロから始まり、呼び出しごとにインクリメントされ続けます。i

于 2013-02-18T00:29:16.860 に答える
0

私の知る限り、クロージャーには常に「親」メソッドのローカル変数への参照が含まれています。違いは、実際に GetFunc2 でこれらの関数を呼び出す前に値をインクリメントしていることです。したがって、呼び出し時には、すでに 5 の値があります。GetFunc3 では、イベントの発生時に値をインクリメントしているため、カウントされます。

于 2013-02-18T00:23:50.457 に答える