2010-03-22 12 views
8

I have 3 (Modifica) si escludono a vicenda IEnumerables che si desidera ripetere. Voglio fare qualcosa di simile:Foreach su un insieme di IEnumerables

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

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

... 

Il modo migliore che viene in mente è:

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

C'è un modo più conciso per fare questo? Sembra che la combinazione di IEnumberables sia banale.

risposta

20

Con LINQ:

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

Per info, la differenza tra il qui e UnionConcat è che Union farà lavoro extra per garantire l'unicità; quindi se non lo fai aspettati i duplicati (o in alternativa: non ti preoccupare) allora Concat è più veloce.

+2

Heh ... Ho iniziato con Concat e poi sono andato a Union e poi ho deciso che non potevo decidere quale usare, quindi ho cancellato la mia risposta. Mi piace il tuo spiegare la differenza in ogni caso meglio. +1 – Randolpho

+0

Esattamente quello che stavo cercando. Grazie! – sdr

+0

@sdr: nota che c'è un compromesso tempo/spazio qui. Supponiamo che tu non avessi tre ma venti liste del genere ciascuna con diecimila elementi. Il tuo metodo (concatenandoli tutti insieme in un'unica grande lista) occupa circa 20 x 10.000 riferimenti extra e richiede 20 operazioni di copia di riferimento di 10KK. Fare 20 concats ingenui, d'altra parte, prende zero riferimenti extra, ma concat nidificati hanno un onere di copia basato sulla loro profondità di nidificazione, quindi ci saranno 10 x 11 x 10K = 110 K copie, non 20K copie. Vedi http://blogs.msdn.com/wesdyer/archive/2007/03/23/all-about-iterators.aspx per i dettagli. –

0

Utilizzare il metodo Unione LINQ.

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

Se si ha assolutamente bisogno di fare questo in un ciclo, andrei con il LINQ. –

+0

Non ci ho pensato. Ma questo non sarà utile in quanto deve confrontarli tutti? Soprattutto perché i tre gruppi si escludono a vicenda. – sdr

+1

Per informazioni, 'Union' sarà più costoso di' Concat' qui, e potrebbe benissimo produrre un risultato diverso all'approccio 'AddRange' (che è essenzialmente un' Concat'). –

0

C'è un motivo per cui è necessario farlo in un ciclo? Se è così, il secondo modo è probabilmente il migliore. Non c'è niente che funzioni come la prima.

A meno che non ci sia un bisogno urgente di farlo in un ciclo, lo farei in tre cicli che sarebbero più efficienti in termini di tempo.

+1

Penso che il "più tempo efficiente" sia un grande reclamo. Si noti inoltre che la creazione di un elenco (con allocazioni, copia, ecc.) Ha un overhead. E c'è molto * è * qualcosa che funzionerà come la prima. –

+0

@Scott, se sta lavorando con ogni auto allo stesso modo, perché vorrebbe più di un ciclo? Questo è solo un codice ridondante. –

3

Come notato in altre risposte, l'Unione fa del lavoro extra per eliminare i duplicati, Concat semplicemente concatena le sequenze. Tuttavia, come ho notato in un commento sopra, ci sono costi di performance per concats profondamente annidati. Si potrebbe considerare anche utilizzando un SelectMany per appiattire un po 'di iteratori:

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

Si potrebbe scoprire che, per essere più flessibile dovrebbe si scopre che in realtà hanno molto più di tre liste di iterazioni.

Problemi correlati