Nel codice C# che ho messo a disposizione c'è un file
CustomCollection.cs
che contiene la definizione delle 3 classi
ChapterCollection, BookCollection e
ShelfCollection:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
[Serializable]
public class ChapterCollection : Collection<Chapter> { }
[Serializable]
public class BookCollection : Collection<Book> { }
[Serializable]
public class ShelfCollection : Collection<Shelf> { }
Grazie alle API create per questo tipo di applicazione, possiamo
velocemente definire un nuovo Book, con i relativi Chapters nel modo
seguente:
// Creo il Book
Book promessiSposi = new Book("I Promessi Sposi", "A. Manzoni", 348);
// Aggiungio tutti i Chapter di questo libro
promessiSposi.Chapters.Add(new Chapter("Prologo"));
promessiSposi.Chapters.Add(new Chapter("Inizio della storia"));
promessiSposi.Chapters.Add(new Chapter("Storia"));
promessiSposi.Chapters.Add(new Chapter("Fine della storia"));
promessiSposi.Chapters.Add(new Chapter("Epilogo"));
Fatto questo, possiamo ciclare tutti i capitoli con un banale for...each:
foreach (Chapter cycle in promessiSposi.Chapters)
Console.WriteLine(cycle.ToString());
Quello su cui voglio porre l'attenzione oggi è che noi potremmo aggiungere i
Chapter in un ordine casuale, per qualsiasi motivo. Quindi, per esempio:
promessiSposi.Chapters.Add(new Chapter("Storia"));
promessiSposi.Chapters.Add(new Chapter("Inizio della storia"));
promessiSposi.Chapters.Add(new Chapter("Prologo"));
promessiSposi.Chapters.Add(new Chapter("Fine della storia"));
promessiSposi.Chapters.Add(new Chapter("Epilogo"));
Il nostro promessiSposi contiene sempre 5 capitoli, ma sono alla
rinfusa. Per ovviare a questo problema, potremmo cambiare la classe che noi
abbiamo utilizzato per implementare l'elenco dei Chapter
(Collection<Chapter>) ed utilizzare la nuova SortedList, disponibile solo nel FX2.0. Questa classe generica
permette l'inserimento di item tramite la classica accoppiata
key e value: sarà compito della classe stessa
mantenere ordinata la struttura dati, utilizzando di
default key come criterio per l'ordinamento stesso.
Quindi, supponiamo di cambiare la dichiarazione di
ChapterCollection da Collection<Chapter> e SortedList<int, Chapter>. A questo punto possiamo creare il Book ed
aggiungere i capitoli uno
ad uno come vogliamo noi e saremo comunque sicuri che verranno inseriti nell'ordine
corretto:
Book promessiSposi = new Book("I Promessi Sposi", "A. Manzoni", 348);
promessiSposi.Chapters.Add(2, new Chapter("Storia"));
promessiSposi.Chapters.Add(1, new Chapter("Inizio della storia"));
promessiSposi.Chapters.Add(0, new Chapter("Prologo"));
promessiSposi.Chapters.Add(3, new Chapter("Fine della storia"));
promessiSposi.Chapters.Add(4, new Chapter("Epilogo"));
foreach (KeyValuePair<int, Chapter> cycle in promessiSposi.Chapters)
Console.WriteLine(cycle.Value);
Il ciclo for...each mostra gli elementi ordinati, indipendentemente
dall'ordine con cui li abbiamo inseriti. Questo comportamento è dovuto al fatto
che il parametro K, nel nostro caso int, implementa
direttamente l'interfaccia IComparable. Se così non fosse, dobbiamo occuparci noi stessi di
scrivere un nostro metodo che il FX utilizzerà per ordinare gli elementi
inseriti nella SortedList. Ne parlo più sotto.
Sulla pagina MSDN dedicata alla classe SortedList<K, T> ci
sono alcune annotazioni per vi riporto:
- SortedList uses less memory than SortedDictionary
- SortedDictionary has faster insertion and removal operations for unsorted
data, O(log n) as opposed to O(n) for SortedList
- If the list is populated all at once from sorted data, SortedList
is faster than SortedDictionary
Scrivere un nostro Comparer
Abbiamo detto più sopra che
normalmente la chiamata al metodo Add sulla nostra SortedList
inserisce il nuovo elemento in base all'ordinamento con stiamo attuando in quel
momento. E' importante ricordarsi che la SortedList lavora esclusivamente sulla
key: quindi, se volessimo personalizzare in qualche modo il
sorting dobbiamo fare in modo che l'oggetto debba essere la key. Quindi,
dobbiamo ancora una volta modificare la dichiarazione della nostra SortedList in
SortedList<Chapter, int>.
[Serializable]
public class ChapterCollection : SortedList<Chapter, int> { }
Adesso dobbiamo modificare la classe Chapter, facendola
aderire all'interfaccia IComparable<Chapter>. Siamo
quindi obbligati a scrivere un metodo come il seguente:
public int CompareTo(Chapter otherChapter)
{
return(_Title.CompareTo(otherChapter._Title));
}
Questo metodo viene utilizzato automaticamente al FX per il sorting,
confrontando la property Title del capitolo. Partendo dal
presupposto di lavorare con una Console Application, il metodo Main appare come
segue:
static void Main()
{
Book promessiSposi = new Book("I Promessi Sposi", "A. Manzoni", 348);
promessiSposi.Chapters.Add(new Chapter("Storia"), 2);
promessiSposi.Chapters.Add(new Chapter("Inizio della storia"), 1);
promessiSposi.Chapters.Add(new Chapter("Prologo"), 0);
promessiSposi.Chapters.Add(new Chapter("Fine della storia"), 3);
promessiSposi.Chapters.Add(new Chapter("Epilogo"), 4);
foreach (KeyValuePair<Chapter, int> cycle in promessiSposi.Chapters)
Console.WriteLine(cycle.Key.Title);
}
// OUTPUT GENERATO
// Epilogo
// Fine della storia
// Inizio della storia
// Prologo
// Storia
In questo momento possiamo modificare il codice di CompareTo per
customizzare il sorting. Ovviamente, sull'oggetto Chapter ha poco senso perchè è
molto semplice e disponiamo solo della property Title.
Immaginate lo stesso lavoro sulla classe Book: a seconda del
codice, possiamo ordinare per titolo, pagine, autore, e così via. Senza contare
che la SortedList espone una property Comparer proprio per questo
scopo.