Обращая множество лямбда-функций - PullRequest
1 голос
/ 16 сентября 2011

Я создал множество лямбда-функций, используя эту функцию

    static IEnumerable<Func<int>> MakeEnumerator(int[] values)
    {
        for (int a = 0; a < values.Length; a++)
        {
            yield return () => Values[a];
        }
    }

Я не могу затем изменить это с помощью LINQ или преобразовать в массив, пока все значения не станут последней функцией. Пример кода (обратите внимание, это просто демонстрирует проблему, это не код в приложении):

        int[] start = {1,2,3};

        IEnumerable<Func<int>> end = MakeEnumerator(start).Reverse<Func<int>>();

        foreach (Func<int> i in end)
        {
            Console.WriteLine(i());
        }

Я думаю, что проблема в функции MakeEnumerator. Как бы я изменил это, чтобы заставить его работать или написать рабочую замену обратной функции.

Ответы [ 2 ]

5 голосов
/ 16 сентября 2011

Проблема в том, что вы захватываете переменную цикла.Все ваши делегаты захватывают одну и ту же переменную, поэтому они всегда будут видеть последнее значение a ... которое будет values.Length + 1 к тому времени, когда вы выполняете делегаты, в ваших случаях использования.Вместо этого вы можете просто скопировать его:

for (int a = 0; a < values.Length; a++)
{
    int copy = a;
    yield return () => Values[copy];
}

В качестве альтернативы (и предпочтительно ИМО) используйте цикл foreach, который в настоящее время требует того же обходного пути:

foreach (int value in values)
{
    int copy = value;
    yield return () => copy;
}

Или еще лучше:

return values.Select(x => (Func<int>)(() => x));

Или:

Func<int, Func<int>> projection = x => () => x;
return values.Select(projection);

См. Сообщение в блоге Эрика Липперта "Закрытие переменной цикла, считающейся вредной" для получения дополнительной информации.Обратите внимание, что поведение foreach вполне может измениться для C # 4.5.

2 голосов
/ 16 сентября 2011

Все ваши лямбда-выражения совместно используют одну и ту же a переменную .
Поскольку вы вызываете их только после завершения цикла, a всегда 3.

Вам нужно присвоить каждому свою собственную переменную:

for (int dontUse = 0; dontUse < values.Length; dontUse++)
{
    int a = dontUse;
    yield return () => Values[a];
}

В этом коде каждое лямбда-выражение получает собственную переменную a (так как она находится внутри цикла), и эти отдельные переменные никогда не будутизменить.

...