У цьому попередженні є дві частини. Перший - це ...
Доступ до змінної 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.
Чи є відповідність ІЛ?
Різний код створює різний ІЛ, тому в цьому сенсі є наслідки для генерованого ІЛ.
1 foreach - набагато більш поширена конструкція, ніж код, який ви опублікували у своєму коментарі. Проблема, як правило, виникає через використання foreach, а не через ручне перерахування. Ось чому зміни foreachв C # 5 допомагають запобігти цій проблемі, але не повністю.