Ci sono due parti in questo avviso. Il primo è ...
Accesso a foreach variabile in chiusura
... che non è valido di per sé ma è contro-intuitivo a prima vista. È anche molto difficile fare bene. (Tanto che l'articolo a cui collego di seguito lo descrive come "dannoso".)
Prendi la tua query, notando che il codice che hai estratto è fondamentalmente una forma espansa di ciò che il compilatore C # (prima di C # 5) genera per foreach1 :
[Non] capisco perché [quanto segue] non è valido:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Bene, è valido sintatticamente. E se tutto ciò che stai facendo nel tuo ciclo è usare il valore di, sallora tutto va bene. Ma chiudersi sporterà a un comportamento controintuitivo. Dai un'occhiata al seguente codice:
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);
}
}
Se esegui questo codice, otterrai il seguente output della console:
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
Questo è quello che ti aspetti.
Per vedere qualcosa che probabilmente non ti aspetti, esegui il codice seguente immediatamente dopo il codice sopra:
foreach (var action in countingActions)
action();
Otterrai il seguente output della console:
s == 5
s == 5
s == 5
s == 5
s == 5
Perché? Perché abbiamo creato cinque funzioni che fanno tutte la stessa identica cosa: stampa il valore di s(che abbiamo chiuso). In realtà, hanno la stessa funzione ("Stampa s", "Stampa s", "Stampa s" ...).
Nel momento in cui andiamo a usarli, fanno esattamente quello che chiediamo: stampare il valore di s. Se guardi l'ultimo valore noto di s, vedrai che è 5. Quindi veniamo s == 5stampati cinque volte sulla console.
Che è esattamente quello che abbiamo chiesto, ma probabilmente non quello che vogliamo.
La seconda parte dell'avvertimento ...
Può avere un comportamento diverso se compilato con versioni diverse del compilatore.
... è quello che è. A partire da C # 5, il compilatore genera codice diverso che "impedisce" che ciò accada tramiteforeach .
Pertanto il codice seguente produrrà risultati diversi in diverse versioni del compilatore:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Di conseguenza, produrrà anche l'avviso R # :)
Il mio primo frammento di codice, sopra, mostrerà lo stesso comportamento in tutte le versioni del compilatore, poiché non lo sto usando foreach(piuttosto, l'ho espanso come fanno i compilatori pre-C # 5).
È per la versione CLR?
Non sono abbastanza sicuro di quello che stai chiedendo qui.
Il post di Eric Lippert dice che la modifica avviene "in C # 5". Cosìpresumibilmente devi scegliere come destinazione .NET 4.5 o versioni successive con un compilatore C # 5 o successivo per ottenere il nuovo comportamento e tutto ciò che precede ottiene il vecchio comportamento.
Ma per essere chiari, è una funzione del compilatore e non della versione .NET Framework.
C'è rilevanza con IL?
Un codice diverso produce un IL diverso, quindi in questo senso ci sono conseguenze per l'IL generato.
1 foreach è un costrutto molto più comune del codice che hai pubblicato nel tuo commento. Il problema si verifica in genere attraverso l'uso di foreach, non tramite l'enumerazione manuale. Ecco perché le modifiche a foreachin C # 5 aiutano a prevenire questo problema, ma non completamente.