Это предупреждение состоит из двух частей. Первый ...
Доступ к переменной foreach при закрытии
... что само по себе недействительно, но на первый взгляд противоречит интуиции. Также очень сложно поступать правильно. (Настолько, что в статье, на которую я ссылаюсь ниже, это описывается как «вредное».)
Возьмите свой запрос, отметив, что код, который вы извлекли, в основном является расширенной формой того, что компилятор C # (до C # 5) генерирует для foreach1 :
Я [не] понимаю, почему [следующее] неверно:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Что ж, синтаксически это действительно так. И если все , что вы делаете в цикле использует значение из sто все хорошо. Но закрытие sприведет к противоречивому поведению. Взгляните на следующий код:
var countingActions = new List<Action>();
var numbers = from n in Enumerable.Range(1, 5)
select n.ToString(CultureInfo.InvariantCulture);
using (var enumerator = numbers.GetEnumerator())
{
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
Console.WriteLine("Creating an action where s == {0}", s);
Action action = () => Console.WriteLine("s == {0}", s);
countingActions.Add(action);
}
}
Если вы запустите этот код, вы получите следующий вывод консоли:
Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5
Это то, чего вы ожидаете.
Чтобы увидеть то, чего вы, вероятно, не ожидаете, запустите следующий код сразу после приведенного выше:
foreach (var action in countingActions)
action();
Вы получите следующий вывод консоли:
s == 5
s == 5
s == 5
s == 5
s == 5
Зачем? Потому что мы создали пять функций, которые делают одно и то же: выводят значение s(которое мы закрыли). На самом деле это одна и та же функция («Печать s», «Печать s», «Печать s» ...).
Когда мы приступаем к их использованию, они делают именно то, что мы просим: выводят значение s. Если вы посмотрите на последнее известное значение s, вы увидите, что это 5. Итак, мы s == 5пять раз выводим на консоль.
Это именно то, о чем мы просили, но, вероятно, не то, что мы хотим.
Вторая часть предупреждения ...
Может иметь разное поведение при компиляции с разными версиями компилятора.
... вот что это такое. Начиная с C # 5, компилятор генерирует другой код, который «предотвращает» это черезforeach .
Таким образом, следующий код будет давать разные результаты в разных версиях компилятора:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Следовательно, он также выдаст предупреждение R # :)
Мой первый фрагмент кода, приведенный выше, будет демонстрировать одинаковое поведение во всех версиях компилятора, поскольку я не использую его foreach(скорее, я расширил его так, как это делают компиляторы до C # 5).
Это для версии CLR?
Я не совсем понимаю, о чем вы здесь спрашиваете.
В сообщении Эрика Липперта говорится, что изменение происходит «в C # 5». Такпредположительно, вам нужно настроить таргетинг на .NET 4.5 или новее с компилятором C # 5 или новее, чтобы получить новое поведение, и все, что было до этого, получает старое поведение.
Но для ясности, это функция компилятора, а не версии .NET Framework.
Есть ли актуальность с Ил?
Разный код порождает другой IL, поэтому в этом смысле для генерируемого IL есть последствия.
1 foreach - гораздо более распространенная конструкция, чем код, который вы разместили в своем комментарии. Проблема обычно возникает при использовании foreach, а не при ручном перечислении. Вот почему изменения foreachв C # 5 помогают предотвратить эту проблему, но не полностью.