40

私は C# を学んでいて、ラムダを理解しようとしています。以下のサンプルでは、​​10 を 10 回出力します。

class Program
{
    delegate void Action();
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i )
            actions.Add(()=>Console.WriteLine(i));

        foreach (Action a in actions)
            a();
    }
}

明らかに、ラムダの背後にある生成されたクラスは、int i変数への参照またはポインターを格納しており、ループが繰り返されるたびに同じ参照に新しい値を割り当てています。C++0x 構文のように、代わりにラムダにコピーを取得させる方法はありますか

[&](){ ... } // Capture by reference

対。

[=](){ ... } // Capture copies
4

4 に答える 4

32

コンパイラが行っていることは、ラムダと、ラムダによってキャプチャされたすべての変数を、コンパイラによって生成されたネストされたクラスにプルすることです。

コンパイル後、例は次のようになります。

class Program
{
        delegate void Action();
        static void Main(string[] args)
        {
                List<Action> actions = new List<Action>();

                DisplayClass1 displayClass1 = new DisplayClass1();
                for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i )
                        actions.Add(new Action(displayClass1.Lambda));

                foreach (Action a in actions)
                        a();
        }

        class DisplayClass1
        {
                int i;
                void Lambda()
                {
                        Console.WriteLine(i);
                }
        }
}

for ループ内でコピーを作成することにより、コンパイラは次のように各反復で新しいオブジェクトを生成します。

for (int i = 0; i < 10; ++i)
{
    DisplayClass1 displayClass1 = new DisplayClass1();
    displayClass1.i = i;
    actions.Add(new Action(displayClass1.Lambda));
}
于 2009-01-16T20:24:22.667 に答える
29

私が見つけた唯一の解決策は、最初にローカル コピーを作成することです。

for (int i = 0; i < 10; ++i)
{
    int copy = i;
    actions.Add(() => Console.WriteLine(copy));
}

しかし、 for ループ内にコピーを配置することがラムダ キャプチャ を持つことと異なる理由を理解するのに苦労していますi

于 2009-01-16T20:08:57.050 に答える
11

唯一の解決策は、ローカル コピーを作成し、ラムダ内でそれを参照することです。クロージャでアクセスされる場合、C# (および VB.Net) のすべての変数には、参照セマンティクスとコピー/値セマンティクスがあります。どちらの言語でも、この動作を変更する方法はありません。

注: 実際には参照としてコンパイルされません。コンパイラは変数をクロージャ クラスに巻き上げ、「i」のアクセスを指定されたクロージャ クラス内のフィールド「i」にリダイレクトします。ただし、多くの場合、それを参照セマンティクスと考える方が簡単です。

于 2009-01-16T20:13:05.670 に答える
4

ラムダ式は、実際には匿名メソッドのシンタックス シュガーにすぎないことを思い出してください。

そうは言っても、本当に探しているのは、匿名メソッドが親スコープでローカル変数を使用する方法です。

これを説明するリンクを次に示します。 http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4

于 2009-01-16T20:20:51.847 に答える