posts - 640, comments - 2604, trackbacks - 144

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

AddRange sulle collection

Purtroppo non c'è nessuna interfaccia che definisca AddRange sulle collection, quindi non c'è ICollection o IList che tenga.

A rimediare elegantemente ci pensano gli extension method che ci permettono di aggiungere AddRange in questo modo:

public static class CollectionExtension
{
    public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> elements)
    {
        foreach (var e in elements)
            list.Add(e);
    }
}

Fatto questo, supponendo di avere una:  IList<qualcosa> collection = new ….;
basterà scrivere:  collection.AddRange(…);

IList infatti deriva da ICollection e quindi l'estensione di ICollection è la più appropriata.

Apparentemente si salva solo una riga di codice ma quando cominciate ad avere parecchio codice che usa Linq la pulizia di codice guadagnata fa una bella differenza.

Print | posted on lunedì 16 agosto 2010 13.40 |

Feedback

Gravatar

# re: AddRange sulle collection


sto riflettendo sui pro e i contro e provando a immaginare e confrontare altre soluzioni.
ecco qui
pro: soluzione decisamente molto pratica
contro: l'interfaccia é per definizione un contratto astratto, quindi estenderla con del codie di implementazione rompe entrambe le premesse

possibili soluzioni alternative che sono riuscito a immaginare:
- usare i tipi concreti per tutte le variabili e argomenti dei metodi privati in modo da poter usare AddRange che espone il tipo concreto.
contro: non risolve il problema per collezioni esposte dai membri pubblici che é meglio sinao tipizzate come interfaccie
- creare un tipo generico su ICollection che implementa l'AddRange meglio se come metodo statico
contro: la sintassi é piu verbosa

naturalmente sono curioso di sentire altre opinioni, altri pro/contro o altre alternative
16/08/2010 14.31 | Luca Minudel
Gravatar

# re: AddRange sulle collection


Raf, grazie mille x l'esempio che offre spunti di riflessione interessanti e l'occasione per me di ripassare la sintassi
16/08/2010 14.33 | Luca Minudel
Gravatar

# re: AddRange sulle collection

Ciao Luca e prego.
Non c'è nessuna violazione. Gli extension method non alterano il contratto ma sono un semplice modo conveniente per usare in modo più naturale i più classici metodi helper statici.
Esporre IEnumerable<T>, ICollection<T> e IList<T> è una pratica comune e da 'best practice'. Questo 'obbliga' ad usare helper statici che possono essere resi ancora più leggibili grazie agli extension method.

Le altre soluzioni che hai esposto sono raramente applicabili. Estendere le suddette interfacce ad esempio rende complesso il deploy e obbliga l'implementazione di metodi come AddRange che invece sono implementabili in modo generico altrove. Le classi base devono essere usate con cautela, perché bruciarsi la gerarchia può non essere possibile o costare molto in futuro.
16/08/2010 23.39 | Raffaele Rialdi
Gravatar

# re: AddRange sulle collection

Raf, scusa ma se lo faccio da VS 2010 NET 4 C# mi dice che non posso creare un extension per un interfaccia Generics ma che posso farlo solo su tipi concreti ... Perche? Io sto facendo un' altra cosa su IList<T>
17/08/2010 1.32 | raffaeu
Gravatar

# re: AddRange sulle collection

Ciao Raffaeu,
probabilmente stai facendo qualche errore perché funziona ovviamente sia su ICollection che IList (non ci sono motivi per non funzionare se il tipo concreto implementa la corrispondente interfaccia).
Controlla di nuovo (e nel caso specifica l'errore esatto).
17/08/2010 8.53 | Raffaele Rialdi
Gravatar

# re: AddRange sulle collection

Ciao Raf!
Riporto quanto hai scritto:

> Esporre IEnumerable<T>, ICollection<T> e IList<T> è una pratica comune e da 'best practice'.

Ecco, io sono dannatamente in accordo con quanto hai scritto.
Non capisco, però, perchè molto codice che mi capita sottomano viene meno a questa 'best practice'.
Ad esempio, molte volte mi capita una cosa del genere:

public interface IRepository<T>
{
IQueryable<T> GetAll();
// ...
}

Attenendosi alle 'best practices' alle quali fai riferimento, il metodo GetAll dovrebbe restituire IList<T>.
Convengo perfettamente che IQueryable<T> offra ben altri strumenti, ma non è un approccio un pò troppo LINQ-oriented?
Da individuo carente di skills quale sono, mi sta sfuggendo qualcosa?
03/09/2010 17.03 | Nicola
Gravatar

# re: AddRange sulle collection

Ottima osservazione. Diciamo che come sempre dipende dal contesto ma in linea di massima non posso che essere daccordo.
Lasciami sparare alcune considerazioni:

IEnumerable<T> è la classe base. Ottima per mille motivi, insufficiente se la usi in windows forms o altri contesti che richiedono interfacce più 'ricche'.
A questo punto uno direbbe: ok, che mi costa, esiste l'extension method ToList() e quindi ho risolto tutto. Invece c'è il grosso problema di performance perché la ToList crea *sempre* una nuova lista enumerando tutti gli elementi anche se l'oggetto fosse già una IList<T>.
Questo per dire che ritornare sempre IEnumerable<T> può essere controproducente.


Passiamo a IQueryable<T>. Nel contesto d'uso di IRepository<T>, come verrà usato quell'oggetto?
Mi viene da dire che Linq sarà sempre utilizzato e quindi sia un'ipotesi di lavoro corretta tranne per il fatto che rende ben più complesso testare quell'oggetto (che fai, un provider solo per i test? argh)
Ma se tornassi IEnumerable<T> potrei usare AsQueryable per ottenere la IQueryable<T> che fa un lavoro molto intellgente, cioè torna IQueryable<T> se l'oggetto è già di quel tipo oppure un wrapper che implementa IQ...<T>.
In buona sostanza hai ragione, esporre pubblicamente IQueryable<T> è superfluo a meno che quel public non sia uno scenario così limitato fa fare poca differenza ed esserci un giustificato ritorno almeno come tempo uomo :)

P.S. Da notare la differenza semantica tra "To" di ToList che esegue una copia, ed "As" di AsQueryable che torna un reference a IQueryable<T> con la migliore soluzione di performance. Ovviamente non è casuale :)
03/09/2010 19.33 | Raffaele Rialdi
Gravatar

# re: AddRange sulle collection

I tuoi post, Raffaele, sono sempre un esempio di completezza.
Faccio un'altra osservazione, ma cerca di essere clemente, perchè probabilmente ciò che dico non ha nè capo nè coda!
Tu dici, "Mi viene da dire che Linq sarà sempre utilizzato...".
Supponiamo, invece, che voglia appoggiarmi ad un O/R Mapper come NHibernate.
Secondo te risulterebbe ancora corretto parlare di IQueryable per l'interfaccia del mio repository.
Perchè, in questo caso, mi chiedo cosa mi vieterebbe di far esporre a quest'ultimo Iesi.Collections.ISet?
Ciò che dico ha un minimo di senso, oppure rientra nel mio inesauribile bagaglio di vaneggiamenti?

Nel caso tu mi risponda "Sì, ma esiste LINQ To NHibernate"...
Bene, è sufficiente quest'ultimo assunto per utilizzare senza esitazione IQueryable?
Grazie ancora Raffaele per le tue delucidazioni.
06/09/2010 10.32 | Nicola
Gravatar

# re: AddRange sulle collection

Ciao Nicola e grazie a te.
Se usi NHibernate ottieni alla fine delle entities che a loro volta saranno probabilmente sottoposte ad altre query usando Linq to objects.
Appoggiarsi invece a un'interfaccia non presente nel framework può essere un vincolo inaccettabile perché potresti decidere in futuro di togliere NHibernate e metterci Entity Framework.
Prendiamo i pro nell'esporre IQueryable o IEnumerable:
- [pro IQ] IQueryable è built-in nel framework e quindi non comporta alcun requirement aggiuntivo (al contrario di ISet)
- [pro IE] D'altra parte la IEnumerable trasformata in IQueryable con l'extension method AsQueryable non ha alcun costo se l'oggetto era già un IQueryable.
- [pro IQ] maggiore leggibilità. Se non sai di avere una IQ e usi AsQueryable verrà creato un oggetto per wrappare il tuo. Magari avresti potuto evitare di usare AsQueryable e perciò avere per certo un oggetto IQ ti lascia più libero di usare i suoi membri.

Infine ricordiamoci sempre che qualsiasi modellazione facciamo, siamo sempre vincolati alla tecnologia che usiamo. Se modelli per C++ o per C# le decisioni *architetturali* che prendiamo possono essere radicalmente differenti, quindi tantovale rassegnarsi a plasmare il proprio modello sui requirements che abbiamo stabilito.
Il vantaggio di queste decisioni è certamente nel tempo uomo risparmiato, che dovrebbe essere sempre in cima alla lista, considerato che il cliente paga in funzione di questo.

A presto
06/09/2010 19.49 | Raffaele Rialdi
Gravatar

# re: AddRange sulle collection

Ok, Raffaele, penso di aver compreso tutta la faccenda.
Grazie ancora e complimenti per il tuo blog!
07/09/2010 8.43 | Nicola

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 7 and 3 and type the answer here:

Powered by: