Si uso:
var strings = new List<string> { "sample" };
foreach (string s in strings)
{
Console.WriteLine(s);
strings.Add(s + "!");
}
el Add
en el foreach
arroja una InvalidOperationException (la colección fue modificada; la operación de enumeración puede no ejecutarse), lo cual considero lógico, ya que estamos tirando de la alfombra debajo de nuestros pies.
Sin embargo, si uso:
var strings = new List<string> { "sample" };
strings.ForEach(s =>
{
Console.WriteLine(s);
strings.Add(s + "!");
});
rápidamente se dispara en el pie haciendo un bucle hasta que arroja una OutOfMemoryException.
Esto me sorprende, ya que siempre pensé que List.ForEach era solo un envoltorio para foreach
o para for
.
¿Alguien tiene una explicación del cómo y el por qué de este comportamiento?
(Inspirado por el bucle ForEach para una lista genérica repetida sin cesar )
foreach
o parafor
". Todavía podría usarfor
. Puede realizar la misma acción en unfor
bucle y generar la misma OutOfMemoryException como resultado.Respuestas:
Es porque el
ForEach
método no usa el enumerador, recorre los elementos con unfor
bucle:(código obtenido con JustDecompile)
Dado que el enumerador no se utiliza, nunca comprueba si la lista ha cambiado y la condición final del
for
ciclo nunca se alcanza porque_size
aumenta en cada iteración.fuente
_size
calcula? Si solo está precalculado, debería ejecutarse una vez para mi ejemplo. Obviamente, está actualizado de alguna manera._version
variable privada en laList<T>
que podría detectar este tipo de escenarios, ya que se actualiza sobre las operaciones que cambian la propia lista.List<T>.ForEach
se implementa porfor
dentro, por lo que no utiliza enumerador y permite modificar la colección.fuente
Porque el ForEach adjunto a la clase List utiliza internamente un bucle for que está directamente adjunto a sus miembros internos, que puede ver descargando el código fuente para el marco .NET.
http://referencesource.microsoft.com/netframework.aspx
Mientras que un bucle foreach es, ante todo, una optimización del compilador, pero también debe operar contra la colección como observador, por lo que si la colección se modifica, lanza una excepción.
fuente
Add
línea constrings.Insert(0, s + "!")
solo imprime 'muestra'. Es extraño que esto no se mencione en absoluto en la documentación.Sabemos de este problema, fue un descuido cuando se escribió originalmente. Desafortunadamente, no podemos cambiarlo porque ahora evitaría que se ejecute este código que anteriormente funcionaba:
La utilidad de este método en sí es cuestionable, como señaló Eric Lippert , por lo que no lo incluimos para .NET para aplicaciones de estilo Metro (es decir, aplicaciones de Windows 8).
David Kean (equipo BCL)
fuente