フォーループパラドックス


ループパラドックス


まず第一に、forループパラドックスは何ですか?
あなたが使うとき、それは起こりますi 内部ループの値async Task . 何が起こるかは、あなたのタスクがi . ループが非常に大きい場合は、いくつかのタスクのいくつかの中間値を取得する可能性があります.
しかし、この動作を示すコードを見てみましょう.
class Program
{
    static async Task Main(string[] args)
    {
        var tasks = new List<Task>();

        for (var i = 0; i <= 9; i++)
        {
            tasks.Add(Task.Run(() =>
            {
                Console.WriteLine(i);
            }));
        }

        await Task.WhenAll(tasks.ToArray());
    }
}
予想される結果は、これらがタスクであり、順序が保証されていないので、ランダムな順序で0から9までのすべての値をコンソールに印刷することです.
しかし、実際に何を得るかを見ましょう.

うーん何かが間違って実際には期待されていない番号10のみを印刷している.

なぜ、これは起こっていますか?


これはクロージャとの一般的な動作です.
tasks.Add(Task.Run(() =>
        {
            Console.WriteLine(i);
        }));
i 実行の時に.

Closures close over variables, not over values.


では、この動作を回避する方法は?


修正プログラムはかなり簡単ですi 新しい変数への値.
class Program
{
    static async Task Main(string[] args)
    {
        var tasks = new List<Task>();

        for (var i = 0; i <= 9; i++)
        {
            // work around for closure behavior
            var j = i;

            tasks.Add(Task.Run(() =>
            {
                // use the new j variable here
                Console.WriteLine(j);
            }));
        }

        await Task.WhenAll(tasks.ToArray());
    }
}
今、forループを繰り返すたびに、新しい変数jを作成します.それぞれのクロージャは別のJ変数で閉じられますi 各反復で変数.
今我々のコンソールでは、期待される結果を得る!

歴史レッスン着信!


この動作は、foreach Cが5をリリースして、この行動を「修正する」とき、ループがC - Chornチームは破壊的な変化をしました.
しかし、for loop 彼らはそのままそれを守ることにした.

より多くの資源


あなたはC Chorseでクロージャについての詳細を読むことができますし、どのように1つだけのジョンSkeetThe Beauty of Closures
私は、この記事を読んで、いくつかのデバッグ時間を節約します!
このポストは愛で書かれた❤️