85 votes

Accès à la variable foreach en fermeture

<blockquote> <p>Accès à la variable foreach en fermeture. Peut avoir un comportement différent lorsqu’il est compilé avec des versions différentes du compilateur.</p> <p><img src="http://i.stack.imgur.com/un2ch.png" alt="enter image description here"></p><p>Je sais pas comment corriger cet avertissement, mais je veux savoir pourquoi obtenir cet avertissement ?</p><p>Est-ce pour <code></code> version ?</p><p>Y a-t-il pertinence avec <code></code> ?</p></blockquote>

135voto

ta.speot.is Points 15157

Il y a deux parties à cet avertissement. Le premier est...

L'accès à l'foreach variable de fermeture

...ce qui n'est pas invalide en soi, mais il est contre-intuitif au premier coup d'œil. Il est également très difficile à faire. (Tellement que l'article que j'ai le lien ci-dessous décrit cela comme "nuisibles".)

Prenez votre requête, en notant que le code que vous avez extrait est fondamentalement une forme élargie de ce que le compilateur C# (avant C# 5) génère pour foreach1:

Je [ne pas] comprendre pourquoi [la suite est pas valide:

string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...

Eh bien, il est valable du point de vue syntaxique. Et si tout ce que vous faites dans votre boucle est à l'aide de la valeur de s alors tout est bon. Mais la fermeture de plus de s conduira à contre-intuitive du comportement. Regardez le 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 suite de sortie de la 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

C'est ce que vous attendez.

Pour voir quelque chose que vous probablement ne vous attendez pas, exécutez le code suivant immédiatement après le code ci-dessus:

foreach (var action in countingActions)
    action();

Vous aurez la suite de sortie de la console:

s == 5
s == 5
s == 5
s == 5
s == 5

Pourquoi? Parce que nous avons créé cinq fonctions toutes faites exactement la même chose: imprimer la valeur de s (dont nous avons fermé au dessus). En réalité, ils sont 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 l' s, vous allez voir que c'est 5. Ainsi nous obtenons s == 5 imprimé cinq fois à la console.

Ce qui est exactement ce que nous avons demandé, mais probablement pas ce que nous voulons.

La deuxième partie de l'avertissement...

Peut avoir des comportements différents lors de la compilation avec les différentes versions de compilateur.

...est-ce que c'est. En commençant avec C# 5, le compilateur génère un code différent qui "empêche" que cela se passe via foreach.

Ainsi, le code suivant va produire des résultats différents en 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 le R# attention :)

Mon premier extrait de code ci-dessus, présentent le même comportement dans toutes les versions du compilateur, car je ne suis pas en utilisant foreach (plutôt, je l'ai développée en dehors de la voie pré-C# 5 les compilateurs n').

Est-ce pour version CLR?

Je ne suis pas tout à fait sûr de ce que vous demandez ici.

Eric Lippert du post dit que le changement arrive "en C# 5". Donc, je présume que vous avez à la cible .NET 4.5 ou version ultérieure avec un C# 5 ou version ultérieure compilateur pour obtenir le nouveau comportement, et de tout ce qui précède que devient l'ancien comportement.

Mais pour être clair, c'est une fonction, le compilateur et pas la .NET Framework version.

Est-il pertinent à l'-t-IL?

De code différents produit différents IL donc, dans ce sens il y a des conséquences pour la IL a généré.

1foreach est un beaucoup plus commun de construire que le code que vous avez posté votre commentaire. Le problème survient généralement par le biais de l'utilisation de foreach, non pas à travers le manuel de l'énumération. C'est pourquoi les changements d' foreach en C# 5 aider à prévenir ce problème, mais pas complètement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X