Este aviso tem duas partes. O primeiro é ...
Acesso a foreach variável no fechamento
... o que não é inválido per se, mas é contra-intuitivo à primeira vista. Também é muito difícil fazer o certo. (Tanto é verdade que o artigo que link abaixo descreve isso como "prejudicial".)
Faça sua consulta, observando que o código que você extraiu é basicamente uma forma expandida do que o compilador C # (antes do C # 5) gera para foreach1 :
Eu [não] entendo porque [o seguinte] não é válido:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Bem, é válido sintaticamente. E se tudo o que você está fazendo em seu loop for usar o valor de, sentão está tudo certo. Mas o fechamento slevará a um comportamento contra-intuitivo. Dê uma olhada no seguinte código:
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 você executar este código, obterá a seguinte saída de 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
Isso é o que você espera.
Para ver algo que você provavelmente não espera, execute o seguinte código imediatamente após o código acima:
foreach (var action in countingActions)
action();
Você obterá a seguinte saída de console:
s == 5
s == 5
s == 5
s == 5
s == 5
Por quê? Porque criamos cinco funções que fazem exatamente a mesma coisa: imprimir o valor de s(que fechamos). Na realidade, têm a mesma função ("Imprimir s", "Imprimir s", "Imprimir s" ...).
No ponto em que vamos usá-los, eles fazem exatamente o que pedimos: imprima o valor de s. Se você olhar para o último valor conhecido de s, verá que é 5. Portanto, somos s == 5impressos cinco vezes no console.
Que é exatamente o que pedimos, mas provavelmente não é o que queremos.
A segunda parte do aviso ...
Pode ter comportamento diferente quando compilado com versões diferentes do compilador.
... é o que é. A partir do C # 5, o compilador gera um código diferente que "impede" que isso aconteça viaforeach .
Assim, o código a seguir produzirá resultados diferentes em diferentes versões do compilador:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Consequentemente, também produzirá o aviso R # :)
Meu primeiro trecho de código, acima, exibirá o mesmo comportamento em todas as versões do compilador, já que não estou usando foreach(em vez disso, expandi-o da maneira que os compiladores pré-C # 5 fazem).
É para a versão CLR?
Não tenho certeza do que você está perguntando aqui.
A postagem de Eric Lippert diz que a mudança acontece "em C # 5". entãopresumivelmente, você deve direcionar o .NET 4.5 ou posterior com um compilador C # 5 ou posterior para obter o novo comportamento, e tudo antes disso obtém o comportamento antigo.
Mas, para ficar claro, é uma função do compilador e não da versão do .NET Framework.
Há relevância com IL?
Código diferente produz IL diferente, portanto, nesse sentido, há consequências para o IL gerado.
1 foreach é uma construção muito mais comum do que o código que você postou em seu comentário. O problema normalmente surge por meio do uso de foreach, não por meio da enumeração manual. É por isso que as alterações foreachno C # 5 ajudam a evitar esse problema, mas não completamente.