posts - 644, comments - 2003, trackbacks - 137

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

Si prega di pensare prima di usare l'extension method ToList()

Sulla scia dei commenti di questo post sull'extension method 'AddRange', ho pensato di sollevare qualche dubbio riguardo all'extension method "ToList()".

Come scrivevo nei commenti dell'altro post ToList() crea sempre una nuova collection, il che è assolutamente corretto considerata la semantica del nome. Tanto per essere chiari se l'oggetto su cui eseguiamo ToList() è già una List<T>, questa verrà comunque ricreata, con un dispendio di performance che può essere considerevole su liste molto grosse e se eseguito ripetutamente.
Il vantaggio di ToList() è di garantire che la lista non sia modificata visto che è una copia della precedente. Attenzione, parlo di copia della lista (shallow-copy) e non degli elementi in essa contenuti (deep-copy).

Una possibile soluzione è quella di creare un nuovo e semplice extension method di nome AsIList().

1 public static class SampleExtension 2 { 3 public static IList<T> AsIList<T>(this IEnumerable<T> set) 4 { 5 var list = set as IList<T>; 6 if(list != null) 7 return list; 8 return set.ToList<T>(); 9 } 10 }

Il banale codice consiste nel tornare IList<T> se l'oggetto di partenza è di quel tipo, altrimenti (se per esempio fosse solo un IEnumerable<T>) crea una nuova lista la ritorna.

L'altrettanto banale test è il seguente.

1 List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6 }; 2 var list1 = numbers.AsIList(); 3 Debug.Assert(object.ReferenceEquals(list1, numbers)); 4 var list2 = numbers.ToList(); 5 Debug.Assert(!object.ReferenceEquals(list2, numbers)); 6 7 IEnumerable<int> even = from n in numbers where n % 2 == 0 select n; 8 var list3 = even.AsIList(); 9 Debug.Assert(!object.ReferenceEquals(list3, even)); 10 var list4 = even.ToList(); 11 Debug.Assert(!object.ReferenceEquals(list4, even));

Nelle prime 5 righe il set di partenza è una List<T> (che è di tipo IList<T>). Di conseguenza:

  • list1 e numbers sono esattamente lo stesso reference, cioè la stessa lista
  • list2 e numbers sono liste differenti per cui i reference non sono uguali

Nelle seconde 5 righe il set di partenza è una IEnumerable<T>, frutto della query su numbers che restituisce i soli numeri pari.
In questo caso il reference even è sempre diverso sia da list3 che da list4.

Happy performant coding :)

Print | posted on lunedì 6 settembre 2010 23:31 |

Feedback

Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Marco, la performance è sempre relativa al contesto. Io non sto dicendo di non usare ToList ma di pensare prima di farlo.
Se fai un ToList una-tantum, ovviamente non te ne accorgi ma se lo fai ad esempio in un datalayer dove moltiplichi per il numero di utenti e magari dentro un loop, fa molta differenza.
07/09/2010 13:19 | Raffaele Rialdi
Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Per fortuna che esiste ReSharper :D
07/09/2010 14:47 | Ugo Lattanzi
Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Si, resharper è un bello strumento ma non deve sostituire il cervello ;-)
07/09/2010 17:01 | Raffaele Rialdi
Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Ciao Omar, la risposta è semplice. No, non puoi fregartene mai di quello che c'è sotto perché chi ha scritto il framework non può sapere come andrai ad usare quella funzione.
Se la usi una volta e la copia dei reference è utile, ToList() è perfetta.
Se la usi un milione di volte e magari ti serve solo recuperare gli elementi con l'indexer, ToList() è devastante.

Il programmatore è un artigiano, con un piccolo intervento può fare un capolavoro o rendere tutto inutilizzabile, al pari di un falegname con un cacciavite in mano.
Se non sai la differenza tra il legno di Balsa e di Noce, l'uso del cacciavite può fare un buco.

Per diminuire i disastri, devi diminuire il raggio di azione, avere strumenti meno potenti e ovviamente dovrai utilizzare mattoncini super-powered :) I super-mattoncini potranno vincolare chi li utilizza impedendo la maggior parte dei possibili errori.

In pratica è come l'operaio di una catena di montaggio che, al contrario dell'artigiano, ha a disposizione un limitatissimo numero di attrezzi che gli consentono di operare solo su uno specifico aspetto che sarà controllato allo step successivo da qualcun altro.

Nel nostro mestiere questi super-mattoncini sono gli oggetti usati nei DSL.
Siamo però ancora piuttosto lontani dalle catene di montaggio del software. Microsoft come tutti gli altri big, creano software secondo procedure artigianali e non industriali.
08/09/2010 20:39 | Raffaele Rialdi
Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Ok, perfetto, in quest'ottica allora mi ritrovo molto meglio nel ragionamento che l'artigiano (in quanto padroneggiatore di un certo lavoro) sia "pagato" anche per pensare (e mi ricollego quindi al titolo del tuo post), mentre l'operaio e colui che usa "solo" ciò che è stato lui preparato...

Così torna tutto di più...

Grazie.
08/09/2010 21:03 | Omar Damiani
Gravatar

# re: Si prega di pensare prima di usare l'extension method ToList()

Si, calza :)
E volendo riprendere il commento di Ugo direi che Resharper è come l'avvitatore. Comodo e funzionale ma può anche spanare la vite ;-)
08/09/2010 22:35 | Raffaele Rialdi
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET