Cet avertissement comporte deux parties. Le premier est ...
Accès à foreach variable en fermeture
... ce qui n'est pas invalide en soi, mais il est contre-intuitif à première vue. Il est également très difficile de bien faire. (À tel point que l'article auquel je renvoie ci-dessous décrit cela comme "nuisible".)
Prenez votre requête, en notant que le code que vous avez extrait est essentiellement une forme développée de ce que le compilateur C # (avant C # 5) génère pour foreach1 :
Je [ne] comprends pas pourquoi [ce qui suit n'est] pas valide:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Eh bien, il est syntaxiquement valide. Et si tout ce que vous faites dans votre boucle utilise la valeur de salors tout est bon. Mais la fermeture smènera à un comportement contre-intuitif. Jetez un œil au code suivant:
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);
}
}
Si vous exécutez ce code, vous obtiendrez la sortie de console suivante:
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
C'est ce à quoi vous vous attendez.
Pour voir quelque chose auquel vous ne vous attendez probablement pas, exécutez le code suivant immédiatement après le code ci-dessus:
foreach (var action in countingActions)
action();
Vous obtiendrez la sortie de console suivante:
s == 5
s == 5
s == 5
s == 5
s == 5
Pourquoi? Parce que nous avons créé cinq fonctions qui font toutes exactement la même chose: afficher la valeur de s(que nous avons clôturée). En réalité, il s'agit de la même fonction ("Imprimer s", "Imprimer s", "Imprimer s" ...).
Au moment où nous allons les utiliser, ils font exactement ce que nous demandons: imprimer la valeur de s. Si vous regardez la dernière valeur connue de s, vous verrez que c'est 5. Nous sommes donc s == 5imprimés cinq fois sur la console.
C'est exactement ce que nous avons demandé, mais probablement pas ce que nous voulons.
La deuxième partie de l'avertissement ...
Peut avoir un comportement différent lorsqu'il est compilé avec différentes versions du compilateur.
... c'est ce que c'est. À partir de C # 5, le compilateur génère un code différent qui "empêche" que cela se produise viaforeach .
Ainsi, le code suivant produira des résultats différents sous différentes versions du compilateur:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Par conséquent, il produira également l'avertissement R # :)
Mon premier extrait de code, ci-dessus, présentera le même comportement dans toutes les versions du compilateur, puisque je n'utilise pas foreach(je l'ai plutôt développé comme le font les compilateurs pré-C # 5).
Est-ce pour la version CLR?
Je ne sais pas trop ce que vous demandez ici.
Le message d'Eric Lippert indique que le changement se produit "en C # 5". Doncvous devez probablement cibler .NET 4.5 ou version ultérieure avec un compilateur C # 5 ou version ultérieure pour obtenir le nouveau comportement, et tout ce qui précède obtient l'ancien comportement.
Mais pour être clair, c'est une fonction du compilateur et non de la version .NET Framework.
Y a-t-il une pertinence avec IL?
Un code différent produit un IL différent, donc dans ce sens il y a des conséquences pour l'IL généré.
1 foreach est une construction beaucoup plus courante que le code que vous avez publié dans votre commentaire. Le problème survient généralement par l'utilisation de foreach, et non par une énumération manuelle. C'est pourquoi les modifications apportées à foreachC # 5 permettent d'éviter ce problème, mais pas complètement.