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 :)