for examples array c# list foreach

examples - foreach list object c#



¿Por qué List<T>.ForEach permite modificar su lista? (4)

Debido a que ForEach unido a la clase List internamente utiliza un bucle for que se adjunta directamente a sus miembros internos, que puede ver al descargar el código fuente para .NET framework.

http://referencesource.microsoft.com/netframework.aspx

Donde como un bucle foreach es ante todo una optimización del compilador, pero también debe operar contra la colección como un observador, por lo que si la colección se modifica arroja una excepción.

Si uso:

var strings = new List<string> { "sample" }; foreach (string s in strings) { Console.WriteLine(s); strings.Add(s + "!"); }

Add in the foreach arroja InvalidOperationException (Se modificó la colección, la operación de enumeración no se puede ejecutar), lo que 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 + "!"); });

de inmediato se dispara en el pie mediante un bucle hasta que arroja una OutOfMemoryException.

Esto es una sorpresa para mí, ya que siempre pensé que List.ForEach era simplemente un envoltorio para foreach o for .
¿Alguien tiene una explicación para el cómo y el porqué de este comportamiento?

(Invocado por ForEach loop para una lista genérica repetida sin fin )


Es porque el método ForEach no usa el enumerador, sino que atraviesa los elementos con un ciclo for :

public void ForEach(Action<T> action) { if (action == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } for (int i = 0; i < this._size; i++) { action(this._items[i]); } }

(código obtenido con JustDecompile)

Como el enumerador no se usa, nunca verifica si la lista ha cambiado, y la condición final del bucle for nunca se alcanza porque _size se incrementa en cada iteración.


Sabemos sobre este tema, fue un descuido cuando se escribió originalmente. Desafortunadamente, no podemos cambiarlo porque ahora evitaría que se ejecute este código que antes funcionaba:

var list = new List<string>(); list.Add("Foo"); list.Add("Bar"); list.ForEach((item) => { if(item=="Foo") list.Remove(item); });

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)


List<T>.ForEach se implementa por dentro, por lo que no usa el enumerador y permite modificar la colección.