C #, затворы и лямбды - PullRequest
       40

C #, затворы и лямбды

1 голос
/ 26 августа 2011

Я не собираюсь задавать вопрос, что такое закрытие. Это закрытие: например:

List<Func<int>> add = new List<Func<int>>();

List<int> coll = new List<int>(){1,2,3,4,5};
foreach (int i in coll)
{
     add.Add(() => i*2);
}

Поскольку замыкания закрываются по переменным, без сомнения, результат будет равен 10 для всех случаев, если мы попытаемся вызвать всю функцию из списка «добавить». Это заставило меня задуматься: если это закрытие, то следующий пример также должен быть закрытием.

//Indirect way of writing the same example
Enumerable.Range(1, 5).ToList().ForEach(x => add.Add(() => x * 2));

Здесь мы также закрываем переменную, поэтому состояние переменной должно быть последним значением переменной, но, как оказалось, это не так. Это не закрытие. Создает ли лямбда свою переменную неизменным образом, т. Е. Как только мы меняем значение x, создается новая переменная для хранения значения?

Ответы [ 3 ]

1 голос
/ 27 августа 2011

Разница в том, что в первом примере используется один и тот же экземпляр i для каждого делегата, поскольку i был захвачен один раз для всего цикла.Во втором примере у вас есть уникальные значения от 1..5 для каждой функции.

Чтобы первый пример работал одинаково, вы можете использовать локальную переменную в цикле следующим образом, теперь x записывается отдельно для каждой функции.

  foreach (int i in coll)
  {
    int x = i;
    add.Add(() => x * 2);
  }

Дополнительная информация

Это сообщение Эрика Липперта на две части по теме

Закрытие переменной цикла, считающейся вредной - Часть 1

Закрытие по контуру переменной считается вредным - Часть 2

0 голосов
/ 27 августа 2011

Во втором примере все еще создается замыкание, захватывающее переменную x.Переменная x является новой переменной для каждого вызова.Демонстрация, возможно, в порядке.

class SomeClass
{
    List<int> col = new List<int> {1,2,3,4,5};

    void SomeFunction()
    {
        for (int x = 0; x < 6; x++)
            ForEachFunction(x);
    }

    void ForEachFunction(int x) 
    {
        // x here is a copy of the variable from the for loop
        col.Add(x);
    }
}

В вашем первом примере, однако, я был определен ранее и использовался для каждого вызова.

0 голосов
/ 27 августа 2011

во втором примере х будет меняться каждый раз, когда вызывается add.Add - в вашем первом примере та же самая переменная "i" попадает в замыкание.

Помимо этого, вы можете думать о замыкании в .net просто как о объекте класса, который захватывает все «внешние» данные, непосредственно не заданные в контексте. В первом примере вы можете создать класс с одним методом (который выполняет i * 2) и одним полем, в котором запоминается ссылка на ваш объект "i".

...