2010-03-22 6 views
8

Tengo 3 (Editar) mutuamente excluyentes IEnumerables que quiero iterar. Quiero hacer algo como esto:Foreach sobre una colección de IEnumerables

IEnumerable<Car> redCars = GetRedCars(); 
IEnumerable<Car> greenCars = GetGreenCars(); 
IEnumerable<Car> blueCars = GetBlueCars(); 

foreach(Car c in (redCars + greenCars + blueCars)) { 
    c.DoSomething(); 
} 

... 

La mejor manera que puedo pensar es:

... 
List<Car> allCars = new List(); 
allCars.AddRange(redCars); 
allCars.AddRange(greenCars); 
allCars.AddRange(blueCars); 
foreach(car in allCars) { 
    ... 
} 
... 

¿Existe una manera más concisa para hacer esto? Parece que combinar IEnumberables debería ser trivial.

Respuesta

20

Con LINQ:

foreach(car in redCars.Concat(greenCars).Concat(blueCars)) { 
    //... 
} 

Para información, la diferencia aquí entre Union y Concat es que Union va a hacer trabajo extra para garantizar la unicidad; así que si no lo hace espera duplicados (o alternativamente: no les moleste) entonces Concat es más rápido.

+2

Heh ... Empecé con Concat y luego fui a Union y luego decidí que no podía decidir cuál usar, así que borré mi respuesta. Me gusta el tuyo explicando la diferencia mejor de todos modos. +1 – Randolpho

+0

Exactamente lo que estaba buscando. ¡Gracias! – sdr

+0

@sdr: Tenga en cuenta que aquí hay una compensación de tiempo/espacio. Supongamos que no tiene tres sino veinte listas, cada una con diez mil elementos. Su método (concatenelos todos juntos en una gran lista) toma alrededor de 20 x 10K referencias adicionales y toma 20 operaciones de copia de referencia de 10K. Hacer 20 conatos ingenuos, por otro lado, no requiere referencias adicionales, pero los cónatos anidados tienen una carga de copia basada en su profundidad de anidación, por lo que habrá 10 x 11 x 10 K = 110 K de copias, no 20 K de copias. Consulte http://blogs.msdn.com/wesdyer/archive/2007/03/23/all-about-iterators.aspx para obtener más información. –

0

Utilice el método LINQ Union.

foreach(Car c in redCars.Union(greenCars).Union(blueCars)) 
{ 
    c.DoSomething(); 
} 
+0

Si necesita hacer esto en un ciclo, iría con el LINQ. –

+0

No pensé en eso. Pero, ¿no será esto exagerado ya que tiene que compararlos a todos? Especialmente porque los tres grupos son mutuamente excluyentes. – sdr

+1

Para información, 'Union' será más caro que' Concat' aquí, y podría muy bien producir un resultado diferente al enfoque 'AddRange' (que es esencialmente un' Concat'). –

0

¿Hay alguna razón por la que deba hacerlo en un ciclo? Si es así, la segunda forma es probablemente la mejor. No hay nada que funcione de la primera manera.

A menos que exista una necesidad imperiosa de hacerlo en un bucle, lo haría en tres bucles que serían más eficientes en el tiempo.

+1

Creo que el "tiempo más eficiente" es un gran reclamo. Tenga en cuenta también que la creación de una lista (con asignaciones, copia, etc.) también tiene una sobrecarga. Y hay mucho * es * algo que funcionará de la primera manera. –

+0

@Scott, si está trabajando con cada auto de la misma manera, ¿por qué iba a querer más de un circuito? Eso es solo código redundante. –

3

Como se señala en otras respuestas, Union hace un trabajo extra para eliminar duplicados, Concat simplemente concatena las secuencias. Sin embargo, como señalé en un comentario anterior, existen costos de rendimiento para los cóncavos profundamente anidados. También es posible considerar el uso de un SelectMany para aplanar un montón de iteradores:

var carLists = new[]{GetRedCars(), GetGreenCars(), GetBlueCars()}; 
var allCars = from carList in carLists 
       from car in carList 
       select car; 
foreach(var c in allCars) { ... } 

Usted puede encontrar que ser más flexibles en caso de que descubra que en realidad tiene mucho más que tres listas a iterar.

Cuestiones relacionadas