2009-09-25 1 views
65

Un collega mi ha chiesto oggi come aggiungere una gamma a una raccolta. Ha una classe che eredita da Collection<T>. C'è una proprietà get-only di quel tipo che contiene già alcuni elementi. Vuole aggiungere gli elementi di un'altra raccolta alla raccolta di proprietà. Come può farlo in modo amichevole con C# 3? (notare il vincolo relativo alla proprietà get-only, che impedisce soluzioni come Unione e riassegnazione.)AddRange to a Collection

Certo, un foreach con Proprietà. Aggiungi funzionerà. Ma uno AddRange di stile List<T> sarebbe molto più elegante.

E 'abbastanza facile scrivere un metodo di estensione:

public static class CollectionHelpers 
{ 
    public static void AddRange<T>(this ICollection<T> destination, 
            IEnumerable<T> source) 
    { 
     foreach (T item in source) 
     { 
      destination.Add(item); 
     } 
    } 
} 

Ma ho la sensazione che sto reinventare la ruota. Non ho trovato nulla di simile in System.Linq o morelinq.

Disegno errato? Basta chiamare Aggiungi? Manca l'ovvio?

+3

Ricorda che la Q di LINQ è "query" e riguarda davvero il recupero dei dati, la proiezione, la trasformazione, ecc.La modifica delle raccolte esistenti in realtà non rientra negli obiettivi previsti da LINQ, motivo per cui LINQ non fornisce nulla di pronto per questo. Ma i metodi di estensione (e in particolare il tuo campione) sarebbero l'ideale per questo. – Levi

+0

Un problema, 'ICollection ' non sembra avere un metodo 'Aggiungi'. http://msdn.microsoft.com/en-us/library/system.collections.icollection_methods(v=vs.100).aspx Tuttavia 'Collection ' ne ha uno. –

+0

@TimGoodman - Questa è l'interfaccia non generica. Vedi http://msdn.microsoft.com/en-us/library/92t2ye13.aspx – TrueWill

risposta

38

No, questo sembra perfettamente ragionevole. C'è un metodo List<T>.AddRange() che fondamentalmente fa proprio questo, ma richiede che la tua collezione sia un concreto List<T>.

+0

Grazie; molto vero, ma la maggior parte delle proprietà pubbliche seguono le linee guida MS e non sono elenchi. – TrueWill

+4

Sì, stavo dando più razionale per il motivo per cui non penso che ci sia un problema con questo. Basta rendersi conto che sarà meno efficiente rispetto alla versione List (dal momento che l'elenco può pre-allocare) –

1

Le classi C5 Generic Collections Library supportano il metodo AddRange. C5 ha un'interfaccia molto più robusta che espone effettivamente tutte le funzionalità delle sue implementazioni sottostanti ed è interfacciabile con le interfacce System.Collections.GenericICollection e IList, il che significa che le collezioni C5 possono essere facilmente sostituite come l'implementazione sottostante.

16

Ricordare che ogni Add verificherà la capacità della raccolta e ridimensionarla quando necessario (più lenta). Con AddRange, la raccolta verrà impostata la capacità e quindi aggiunto gli elementi (più veloce). Questo metodo di estensione sarà estremamente lento, ma funzionerà.

+3

Per aggiungere a questo, ci sarà anche una notifica di modifica della raccolta per ogni aggiunta, al contrario di una notifica di massa con AddRange. –

0

È possibile aggiungere l'intervallo IEnumerable a un elenco, quindi impostare ICollection = nell'elenco.

+3

Mentre funziona funzionalmente, interrompe le linee guida Microsoft per rendere le sole proprietà di lettura (http://msdn.microsoft.com/en-us/library/ms182327.aspx) –

24

Provare a eseguire il casting su Elenco nel metodo di estensione prima di eseguire il ciclo. In questo modo puoi sfruttare le prestazioni di List.AddRange.

public static void AddRange<T>(this ICollection<T> destination, 
           IEnumerable<T> source) 
{ 
    List<T> list = destination as List<T>; 

    if (list != null) 
    { 
     list.AddRange(source); 
    } 
    else 
    { 
     foreach (T item in source) 
     { 
      destination.Add(item); 
     } 
    } 
} 
+0

Potrebbe essere una domanda un po 'nasca ma cosa succede quando la collezione '' 'destination''' non può essere inserita in un' '' List '' '? '' 'List''' diventa automaticamente' '' null''' o viene lanciata un'eccezione? –

+1

L'operatore 'as' non getterà mai. Se 'destination' non può essere lanciato,' list' sarà nullo e il blocco 'else' verrà eseguito. – rymdsmurf

+3

arrgggh! Scambia i rami delle condizioni, per amore di tutto ciò che è santo! – nicodemus13

15

Dal .NET4.5 se volete uno-liner si can use System.Collections.Generic ForEach.

source.ForEach(o => destination.Add(o)); 

o anche più breve

source.ForEach(destination.Add); 

vista delle prestazioni è uguale per ogni ciclo (zucchero sintattico).

anche non mi Provate ad assegnare piace

var x = source.ForEach(destination.Add) 

causa ForEach è vuoto.

+5

Personalmente sono con Lippert su questo: http://blogs.msdn.com/b/ericlippert/archive /2009/05/18/foreach-vs-foreach.aspx – TrueWill

+1

Dovrebbe essere source.ForEach (destination.Add)? – Frank

+0

@Frank Ciao Frank, corretto grazie per aver notato :) –