Подтвердить что ты не робот

Как определить функцию лямбда для копирования копии вместо ссылки на С#?

Я изучаю С#, и я пытаюсь понять лямбда. В этом примере ниже он печатает 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 и присваивает новое значение той же ссылке каждый раз, когда цикл повторяется. Есть ли способ заставить lamda захватить копию вместо этого, как синтаксис С++ 0x

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

против.

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

Ответ 1

Что делает компилятор, это тянуть вашу лямбду и любые переменные, захваченные лямбдой, в генерируемый компилятором вложенный класс.

После компиляции ваш пример выглядит примерно так:

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

Ответ 2

Единственное решение - сделать локальную копию и ссылку в пределах лямбда. Все переменные в С# (и VB.Net) при доступе в закрытии будут иметь семантику ссылок и семантику копирования/значения. Невозможно изменить это поведение на любом языке.

Примечание. Он не компилируется в качестве ссылки. Компилятор подталкивает переменную в класс замыкания и перенаправляет обращения "i" в поле "i" внутри данного класса замыкания. Однако часто легче думать об этом как о контрольной семантике.

Ответ 3

Единственное решение, которое я смог найти, - сначала сделать локальную копию:

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

Но у меня возникли проблемы с пониманием того, почему размещение копии внутри цикла for не зависит от наличия лямбда-захвата i.

Ответ 4

Помните, что лямбда-выражения на самом деле являются только синтаксическим сахаром для анонимных методов.

Говоря о том, что вы действительно ищете, как анонимные методы используют локальные переменные в родительской области.

Здесь ссылка, описывающая это. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4