Supponiamo di lavorare con due classi del tipo seguente:
e di voler utilizzare Linq To Objects per eseguire delle operazioni sul nostro insieme di dati, composto da un array di oggetti Documento:
1 Documento[] documenti = new Documento[]{
2 new Documento ("10",new Cliente {Nome="Pietro",Cognome ="Libro",CodiceFiscale="ABA"}),
3 new Documento ("2",new Cliente {Nome="Pietro",Cognome ="Bianchi",CodiceFiscale="ABB"}),
4 new Documento ("1",new Cliente {Nome="Giuseppe",Cognome ="Rossi",CodiceFiscale="ABC"}),
5 new Documento ("1",new Cliente {Nome="Gabriele",Cognome ="Bianchi",CodiceFiscale="ABD"})};
Supponiamo di voler utilizzare una query expression per ottenere una lista (nello specifico un oggetto che implementi l'interfaccia IEnumerable<T>, o IOrderedEnumerable<T> che estende IEnumerable<t>), che possa essere "esplorata" mediante uno statement foreach:
1 var result =
2 from d in documenti
3 orderby d.Numero
4 select d;
Eseguendo il tutto in un'applicazione console otterremmo:
Che non è proprio quello che ci aspettiamo dato che 2 viene prima di 10. Il problema nasce dal fatto che nel nostro modello ad oggetti, abbiamo definito il campo Numero, della classe Documento, come String. A questo punto potremmo optare tra almeno due soluzioni: procediamo con un refactoring del codice e cambiamo il tipo del membro Numero, o utilizziamo un overload dell'Extension Method OrderBy (o OrderByDescending). Più precisamente, utilizzeremo l'overload seguente:
1 public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
2 this IEnumerable<TSource> source,
3 Func<TSource, TKey> keySelector,
4 IComparer<TKey> comparer
5 )
6
Il quale esegue l'ordinamento ascendente di una sequenza di dati utilizzando uno specifico Comparer. Nello specifico, questo sarà così definito:
1 public class DocumentoComparer : IComparer<Documento>
2 {
3 #region IComparer<Documento> Members
4
5 public int Compare(Documento x, Documento y)
6 {
7 if (x.Numero.Equals(y.Numero))
8 {
9 //Ordina in base al nominativo del cliente
10 return string.Compare(x.Cliente.Cognome, y.Cliente.Cognome);
11 }
12 else
13 {
14 if (int.Parse(x.Numero) < int.Parse(y.Numero))
15 return -1;
16 else
17 return 1;
18 }
19 }
20
21 #endregion
22 }
Nel caso in cui siano presenti due documenti con lo stesso numero, magari perchè di tipo diverso, l'ordinamento viene effettuato in base all'ordinamento lessicografico della proprietà Cognome del Cliente, altrimenti le stringhe Numero sono convertite nei rispettivi valori interi, quindi eseguito il confronto.
A questo punto, mediante method syntax, possiamo scrivere:
1 var resultMethod =
2 documenti
3 .OrderBy(d => d, new DocumentoComparer());
Ottenendo:
Nel caso in cui non sia fornito un comparatore o che Comparer sia nullo verrà utilizzata la proprietà Default, di Comparer<T>, ovvero Comparer<TKey>.Default, ma se il tipo T (come ad esempio Documento) non implementa ne l'interfaccia IComparable<T> ne IComparable, in fase di esecuzione verrà generata un'eccezione.