Actualización 17/07/2012: Aparentemente a partir de C # 5.0, el comportamiento foreach
descrito a continuación ha cambiado y " el uso de una foreach
variable de iteración en una expresión lambda anidada ya no produce resultados inesperados " . Esta respuesta no se aplica a C # ≥ 5.0 .
@John Skeet y todos los que prefieren la palabra clave foreach.
El problema con "foreach" en C # anterior a 5.0 , es que es inconsistente con la forma en que el equivalente "para la comprensión" funciona en otros idiomas, y con cómo esperaría que funcionara (la opinión personal aquí se menciona solo porque otros han mencionado su opinión sobre la legibilidad). Consulte todas las preguntas sobre " Acceso al cierre modificado ", así como " Cierre sobre la variable de bucle considerado dañino ". Esto es solo "dañino" debido a la forma en que se implementa "foreach" en C #.
Tome los siguientes ejemplos utilizando el método de extensión funcionalmente equivalente al de la respuesta 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);
}
}
}
Disculpas por el ejemplo excesivamente inventado. Solo estoy usando Observable porque no es del todo descabellado hacer algo como esto. Obviamente, hay mejores formas de crear este observable, solo estoy tratando de demostrar un punto. Por lo general, el código suscrito al observable se ejecuta de forma asíncrona y potencialmente en otro hilo. Si usa "foreach", esto podría producir resultados muy extraños y potencialmente no deterministas.
La siguiente prueba con el método de extensión "ForEach" pasa:
[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()));
}
Lo siguiente falla con el error:
Esperado: equivalente a <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Pero fue: <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()
.