1

次のプログラムが出力を提供する理由を理解しようとしています。私はそれが参照や値と関係があることを知っていますが、用語も、どこに行けばもっと学ぶべきかわかりません。

        for (int x = 0; x < 2; x++)
        {
            int y = x;
            new Thread(new ThreadStart(() =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine("Thread sees x = {0}, y = {1}", x, y);
                })).Start();
        }
        Thread.Sleep(1000);

出力:

Thread sees x = 2, y = 0
Thread sees x = 2, y = 1

この種のことを説明する参考資料をいただければ幸いです。

4

3 に答える 3

2

注: これは実際には完全な話ではなく、おそらく何か間違っている可能性がありますが、アイデアはまだ有効です。

これは、クロージャが原因で発生します。

クロージャは for ループで発生します (以前は foreach ループで発生していましたが、C# 5 で変更されました)。コンパイルされるのは次のようなものです。

int x = 0;
while (x < 2)
        {
            int y = x;
            new Thread(new ThreadStart(() =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine("Thread sees x = {0}, y = {1}", x, y);
                })).Start();
            x++;
        }
        Thread.Sleep(1000);

for ループの範囲外に存在するためx、ラムダは取り込まれませんx。ただし、yのスコープは for ループの外側で終了するため、ラムダは変数を保持する必要があります。それまでになくなってしまうため、実行時に後で変数を取得することはできません。実際、ラムダは実行時に次のようになります。

最初のループ実行:

() => Thread.Sleep(100); Console.WriteLine("Thread sees x = {0}, y = {1}, x, 0);

2 回目のループ実行:

() => Thread.Sleep(100); Console.WriteLine("Thread sees x = {0}, y = {1}, x, 1);

そして、ラムダが実際に実行されるまでに、xはすでに2であり、それを取り込まなかったので、 として読み取ります2

于 2013-03-13T19:56:50.580 に答える
0

これは、C#4.0以前でのクロージャーの仕組みです。変数xは、C#5.0ではより直感的です(指摘されているように、ループではなく、foreachに対して実際に)。

クロージャは、反復ごとに同じ変数をキャプチャします。したがって、最後の値になります。

        List<Action> actions = new List<Action>();
        for (int i = 0; i < 10; i++)
        {
            Action anonymousFunction = () => Console.WriteLine(i);                
            actions.Add(anonymousFunction);
        }
        foreach (var action in actions)
        {
            action();//prints out the last value every time
        }

ただし、新しい値をコピーすることで動作を変更できます。

        List<Action> actions = new List<Action>();
        for (int i = 0; i < 10; i++)
        {
            int copyOfOriginalValue = i;
            Action anonymousFunction = () => Console.WriteLine(copyOfOriginalValue);                
            actions.Add(anonymousFunction);
        }
        foreach (var action in actions)
        {
            action();//prints out unique values
        }
于 2013-03-13T19:47:12.683 に答える
0

オブジェクトのコンストラクターに匿名メソッド/ラムダを渡していThreadStartます。xコンパイラは、この匿名メソッドを含むクラスを作成yし、メソッド内のローカル変数から、このコンパイラが作成したクラス内のクラス変数に昇格させます。そのため、これらの変数の値にアクセスできます。

この MSDN の記事を参照してください: http://msdn.microsoft.com/en-us/library/0yw3tz5k(v=vs.80).aspx

于 2013-03-13T19:48:25.990 に答える