net getenumerator linq foreach ienumerable

getenumerator - foreach list linq



LINQ equivalente de foreach para IEnumerable<T> (21)

Mantenga sus efectos secundarios fuera de mi IEnumerable

Me gustaría hacer el equivalente de lo siguiente en LINQ, pero no puedo entender cómo:

Como otros han señalado here y en el extranjero, se espera que los métodos LINQ e IEnumerable estén libres de efectos secundarios.

¿Realmente desea "hacer algo" para cada elemento en el Entregable de I? Entonces foreach es la mejor opción. La gente no se sorprende cuando los efectos secundarios ocurren aquí.

foreach (var i in items) i.DoStuff();

Apuesto a que no quieres un efecto secundario

Sin embargo, en mi experiencia, los efectos secundarios no suelen ser necesarios. La mayoría de las veces, hay una consulta LINQ simple que espera ser descubierta acompañada por una respuesta de StackOverflow.com de Jon Skeet, Eric Lippert o Marc Gravell, que explica cómo hacer lo que quiere.

Algunos ejemplos

Si en realidad solo está agregando (acumulando) algún valor, entonces debería considerar el método de extensión Aggregate .

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

Quizás desee crear un nuevo IEnumerable partir de los valores existentes.

items.Select(x => Transform(x));

O quizás quieras crear una tabla de consulta:

items.ToLookup(x, x => GetTheKey(x))

La lista (el juego de palabras no tiene la intención del todo) de posibilidades sigue y sigue.

Me gustaría hacer el equivalente de lo siguiente en LINQ, pero no puedo entender cómo:

IEnumerable<Item> items = GetItems(); items.ForEach(i => i.DoStuff());

¿Cuál es la sintaxis real?


Ahora tenemos la opción de ...

ParallelOptions parallelOptions = new ParallelOptions(); parallelOptions.MaxDegreeOfParallelism = 4; #if DEBUG parallelOptions.MaxDegreeOfParallelism = 1; #endif Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

Por supuesto, esto abre una nueva lata de gusanos de hilo.

ps (perdón por las fuentes, es lo que decidió el sistema)


Como ya lo han indicado numerosas respuestas, usted puede agregar fácilmente un método de extensión de este tipo. Sin embargo, si no quiere hacer eso, aunque no conozco nada como esto en el BCL, todavía hay una opción en el espacio de nombres del System , si ya tiene una referencia a la Extensión reactiva (y si no la tiene). t, deberías tener):

using System.Reactive.Linq; items.ToObservable().Subscribe(i => i.DoStuff());

Aunque los nombres de los métodos son un poco diferentes, el resultado final es exactamente lo que está buscando.


De acuerdo con PLINQ (disponible desde .Net 4.0), puede hacer una

IEnumerable<T>.AsParallel().ForAll()

para hacer un bucle foreach paralelo en un IEnumerable.


El propósito de ForEach es causar efectos secundarios. IEnumerable es para la enumeración perezosa de un conjunto.

Esta diferencia conceptual es bastante visible cuando se considera.

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

Esto no se ejecutará hasta que hagas un "conteo" o un "ToList ()" o algo así. Claramente no es lo que se expresa.

Debe usar las extensiones IEnumerable para configurar cadenas de iteración, definiendo el contenido por sus respectivas fuentes y condiciones. Los árboles de expresión son poderosos y eficientes, pero debes aprender a apreciar su naturaleza. Y no solo para la programación a su alrededor para guardar algunos caracteres que anulan la evaluación perezosa.


Esta abstracción de "enfoque funcional" se filtra a lo grande. Nada en el nivel de lenguaje previene los efectos secundarios. Mientras pueda hacer que llame a su lambda / delegado para cada elemento del contenedor, obtendrá el comportamiento "ForEach".

Aquí, por ejemplo, una forma de fusionar srcDictionary en destDictionary (si la clave ya existe - sobrescribe)

Esto es un hack y no debe usarse en ningún código de producción.

var b = srcDictionary.Select( x=> { destDictionary[x.Key] = x.Value; return true; } ).Count();


ForEach también puede estar encadenado , solo vuelva a colocarlo en la línea piloto después de la acción. permanecer fluido

Employees.ForEach(e=>e.Act_A) .ForEach(e=>e.Act_B) .ForEach(e=>e.Act_C); Orders //just for demo .ForEach(o=> o.EmailBuyer() ) .ForEach(o=> o.ProcessBilling() ) .ForEach(o=> o.ProcessShipping()); //conditional Employees .ForEach(e=> { if(e.Salary<1000) e.Raise(0.10);}) .ForEach(e=> { if(e.Age >70 ) e.Retire();});

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action) { foreach (T item in enu) action(item); return enu; // make action Chainable/Fluent }

El código de Edición 2 anterior está funcionando, pero una mejor versión está usando this .

Editar a continuación fue un ejemplo incorrecto, señalado por Taemyr. Muchas gracias

Employees.ForEach(e=>e.Salary = e.Salary * 2) .Where (e=> e.Salary > 10000) .Average(e=> e.Salary);


Fredrik ha proporcionado la solución, pero vale la pena considerar por qué esto no está en el marco para empezar. Creo que la idea es que los operadores de consultas LINQ deberían estar libres de efectos secundarios, ajustándose a una manera razonablemente funcional de ver el mundo. Claramente, ForEach es exactamente lo contrario: una construcción basada puramente en efectos secundarios.

Eso no quiere decir que esto sea algo malo, solo pensar en las razones filosóficas detrás de la decisión.


Hay un lanzamiento experimental por Microsoft de Interactive Extensions to LINQ (también en NuGet , vea el perfil de RxTeams para más enlaces). El video del canal 9 lo explica bien.

Sus documentos solo se proporcionan en formato XML. He ejecutado esta documentación en Sandcastle para permitir que esté en un formato más legible. Descomprima el archivo de documentos y busque index.html .

Entre muchos otros beneficios, proporciona la implementación esperada de ForEach. Te permite escribir código como este:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 }; numbers.ForEach(x => Console.WriteLine(x*x));


Inspirado por Jon Skeet, he ampliado su solución con lo siguiente:

Método de extensión:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector) { foreach (var item in source) { var target = keySelector(item); applyBehavior(target); } }

Cliente:

var jobs = new List<Job>() { new Job { Id = "XAML Developer" }, new Job { Id = "Assassin" }, new Job { Id = "Narco Trafficker" } }; jobs.Execute(ApplyFilter, j => j.Id);

. . .

public void ApplyFilter(string filterId) { Debug.WriteLine(filterId); }



Mucha gente lo mencionó, pero tuve que escribirlo. ¿No es esto más claro / más legible?

IEnumerable<Item> items = GetItems(); foreach (var item in items) item.DoStuff();

Corto y sencillo (st).


No estoy de acuerdo con la idea de que los métodos de extensión de enlace deban estar libres de efectos secundarios (no solo porque no lo son, cualquier delegado puede realizar efectos secundarios).

Considera lo siguiente:

public class Element {} public Enum ProcessType { This = 0, That = 1, SomethingElse = 2 } public class Class1 { private Dictionary<ProcessType, Action<Element>> actions = new Dictionary<ProcessType,Action<Element>>(); public Class1() { actions.Add( ProcessType.This, DoThis ); actions.Add( ProcessType.That, DoThat ); actions.Add( ProcessType.SomethingElse, DoSomethingElse ); } // Element actions: // This example defines 3 distict actions // that can be applied to individual elements, // But for the sake of the argument, make // no assumption about how many distict // actions there may, and that there could // possibly be many more. public void DoThis( Element element ) { // Do something to element } public void DoThat( Element element ) { // Do something to element } public void DoSomethingElse( Element element ) { // Do something to element } public void Apply( ProcessType processType, IEnumerable<Element> elements ) { Action<Element> action = null; if( ! actions.TryGetValue( processType, out action ) ) throw new ArgumentException("processType"); foreach( element in elements ) action(element); } }

Lo que muestra el ejemplo es realmente un tipo de enlace tardío que permite invocar una de las muchas acciones posibles que tienen efectos secundarios en una secuencia de elementos, sin tener que escribir una construcción de gran interruptor para descodificar el valor que define la acción y traducir en su método correspondiente.


No hay una extensión de IEnumerable para IEnumerable ; solo para la List<T> . Para que pudieras hacer

items.ToList().ForEach(i => i.DoStuff());

Alternativamente, escriba su propio método de extensión ForEach:

public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action) { foreach(T item in enumeration) { action(item); } }


Para VB.NET debes usar:

listVariable.ForEach(Sub(i) i.Property = "Value")


Podría usar la extensión FirstOrDefault() , que está disponible para IEnumerable<T> . Al devolver false desde el predicado, se ejecutará para cada elemento, pero no le importará que en realidad no encuentre una coincidencia. Esto evitará la ToList() .

IEnumerable<Item> items = GetItems(); items.FirstOrDefault(i => { i.DoStuff(); return false; });


Si desea actuar como rollos de enumeración, debe ceder cada elemento.

public static class EnumerableExtensions { public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action) { foreach (var item in enumeration) { action(item); yield return item; } } }


Si está haciendo esto, por ejemplo, porque necesita el índice en su iteración, siempre podría usar una construcción Where:

linqObject.Where((obj, index) => { DoWork(obj, index); return true; }).ToArray(); //MUST CALL ToArray() or ToList() or something to execute the lazy query, or the loop won''t actually execute

Esto tiene la ventaja adicional de que la matriz original se devuelve "sin cambios" (los objetos a los que hace referencia la lista son los mismos, aunque es posible que no tengan los mismos datos), que a menudo es deseable en metodologías de programación funcional / en cadena como LINQ.


Sin embargo, otro ejemplo de ForEach

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses) { var workingAddresses = new List<AddressEntry>(); addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a))); return workingAddresses; }


Tomé el método de Fredrik y modifiqué el tipo de retorno.

De esta manera, el método admite la ejecución diferida como otros métodos LINQ.

EDITAR: Si esto no estaba claro, cualquier uso de este método debe terminar con ToList () o cualquier otra forma de forzar el método para que funcione en el enumerable completo. De lo contrario, la acción no se llevaría a cabo!

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action) { foreach (T item in enumeration) { action(item); yield return item; } }

Y aquí está la prueba para verlo:

[Test] public void TestDefferedExecutionOfIEnumerableForEach() { IEnumerable<char> enumerable = new[] {''a'', ''b'', ''c''}; var sb = new StringBuilder(); enumerable .ForEach(c => sb.Append("1")) .ForEach(c => sb.Append("2")) .ToList(); Assert.That(sb.ToString(), Is.EqualTo("121212")); }

Si elimina la Lista de Toques () al final, verá que la prueba falla, ya que el StringBuilder contiene una cadena vacía. Esto se debe a que ningún método obligó a ForEach a enumerar.


Actualización 7/17/2012: Aparentemente a partir de C # 5.0, el comportamiento de foreach descrito a continuación ha cambiado y " el uso de una variable de iteración foreach 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 # antes de 5.0 , es que es inconsistente con la forma en que funciona el equivalente de "comprensión" en otros idiomas, y con la forma en que esperaría que funcionara (opinión personal expresada aquí porque otros han mencionado su opinión sobre la legibilidad). Consulte todas las preguntas relacionadas con " Acceso al cierre modificado ", así como " Cierre sobre la variable de bucle considerada dañina ". Esto solo es "perjudicial" debido a la forma en que "foreach" se implementa 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 artificial. Solo uso Observable porque no es del todo extraño hacer algo como esto. Obviamente, hay mejores formas de crear este observable, solo intento demostrar un punto. Normalmente, el código suscrito al observable se ejecuta de forma asíncrona y potencialmente en otro hilo. Si se usa "foreach", esto podría producir resultados muy extraños y potencialmente no deterministas.

La siguiente prueba que utiliza 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())); }