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

Почему эти результаты linq отличаются?

1-й оператор:

IEnumerable<char> query = "Not what you might expect";

query = query.Where (c => c != 'a');
query = query.Where (c => c != 'e');
query = query.Where (c => c != 'i');
query = query.Where (c => c != 'o');
query = query.Where (c => c != 'u');

Вывод String.Join("", query): "Nt wht y mght xpct"

2-й оператор:

query = "Not what you might expect";

foreach (char vowel in "aeiou")
    query = query.Where (c => c != vowel);

Выход String.Join("", query): "Not what yo might expect"

Выходы из этих операторов различны. Может ли кто-нибудь объяснить, почему?

4b9b3361

Ответ 1

Если вы используете версию С# ниже 5.0 (где это исправлено), вот почему:

Лямбда в вашем запросе захватывает переменную цикла vowel.
Поскольку Linq любит использовать отложенное выполнение, значение этой ссылки не считывается до тех пор, пока запрос не будет выполнен (итерацией по нему), который после завершения цикла foreach завершен. В этот момент самое последнее значение vowel равно u, поэтому вы получаете неожиданный вывод.

Вы можете обойти это, скопировав значение в другую временную переменную (или обновив до С# 5.0).

Попробуйте следующее:

query = "Probably what you might expect";

foreach (char vowel in "aeiou") {
    char currentVowel = vowel;
    query = query.Where (c => c != currentVowel );
}

Ответ 2

Это потому, что вы создаете закрытие по переменной vowel, которая изменяется во времени. Сохраните его значение в отдельной переменной, и он будет работать:

query = "Not what you might expect";

foreach (char vowel in "aeiou")
{
    var current = vowel;
    query = query.Where (c => c != current);
}

Ответ 3

Читайте о закрытии. Если вы используете .NET 4.0 и ниже, вы получите другой результат. В .NET 4.5 это поведение изменяется (фиксировано). См. Также, как компилятор расширяет foreach.