Atualização 7/17/2012: Aparentemente, a partir do C # 5.0, o comportamento foreach
descrito abaixo foi alterado e " o uso de uma foreach
variável de iteração em uma expressão lambda aninhada não produz mais resultados inesperados " . Esta resposta não se aplica ao C # ≥ 5.0 .
@ John Skeet e todos que preferem a palavra-chave foreach.
O problema com "foreach" em C # anterior à 5.0 é que ele é inconsistente com o modo como a "compreensão" equivalente funciona em outros idiomas e com o que eu esperaria que funcionasse (opinião pessoal declarada aqui apenas porque outros mencionaram seus opinião sobre legibilidade). Consulte todas as perguntas sobre " Acesso ao fechamento modificado " e " Variável sobre o loop considerada prejudicial ". Isso é apenas "prejudicial" devido à maneira como "foreach" é implementado em C #.
Pegue os exemplos a seguir, usando o método de extensão funcionalmente equivalente ao da resposta de @Fredrik Kalseth.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
Desculpas pelo exemplo excessivamente artificial. Só estou usando o Observable porque não é totalmente buscado fazer algo assim. Obviamente, existem maneiras melhores de criar isso observável, estou apenas tentando demonstrar um ponto. Normalmente, o código inscrito no observável é executado de forma assíncrona e potencialmente em outro encadeamento. Se usar "foreach", isso pode produzir resultados muito estranhos e potencialmente não determinísticos.
O seguinte teste usando o método de extensão "ForEach" passa:
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
O seguinte falha com o erro:
Esperado: equivalente a <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Mas foi: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
ForEach()
.