Si uso:
var strings = new List<string> { "sample" };
foreach (string s in strings)
{
  Console.WriteLine(s);
  strings.Add(s + "!");
}el Adden el foreacharroja 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 foreacho 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 )

foreacho parafor". Todavía podría usarfor. Puede realizar la misma acción en unforbucle y generar la misma OutOfMemoryException como resultado.Respuestas:
Es porque el
ForEachmétodo no usa el enumerador, recorre los elementos con unforbucle:(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
forciclo nunca se alcanza porque_sizeaumenta en cada iteración.fuente
_sizecalcula? Si solo está precalculado, debería ejecutarse una vez para mi ejemplo. Obviamente, está actualizado de alguna manera._versionvariable 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>.ForEachse implementa porfordentro, 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
Addlí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