Le interfacce ICollection, IList e
ICloneable
Partendo dal codice che abbiamo visto nel post
precedente, cominciamo a fare un po' di refactoring e vedere di rendere la
nostra classe Shelf aderente ad alcune delle interfacce standard di .NET. Le
classi con cui cominceremo sono ICollection, ICloneable e IList. Come abbiamo detto in un post precedente,
aderire ad una certa interfaccia (o contract, contratto) significa
fornire l'implementazione per ciascuno dei membri previsti dall'interfaccia
stessa. La prima cosa da sapere quando dobbiamo fare un lavoro di questo tipo
quindi è sapere cosa prevede effettivamente ciascuna interfaccia e scrivere il
codice necessario. Cominciamo con ICollection: questa
interfaccia prevede tre proprietà (Count, SyncRoot e IsSynchronized) e 1 solo metodo (CopyTo). Se vediamo la documentazione su MSDN2, però,
scopriamo che ICollection deriva da IEnumerable, per cui dobbiamo
per forza di cose implementare anche quest'ultima interfaccia, che comprende
solo il metodo GetEnumerator
.
Possiamo quindi far aderire la nostra classe Shelf al contratto previsto da
ICollection aggiungendo il codice seguente:
#region "Membri per ICollection"
// Siccome Shelf deve implementare l'interfaccia ICollection
// aggiungo i membri necessari
public int Count
{ get { return (_Contents.Length); } }
public void CopyTo(Array array, int index)
{ }
public object SyncRoot
{ get { return (null); } }
public bool IsSynchronized
{ get { return (true); } }
public IEnumerator GetEnumerator()
{
return
(_Contents.GetEnumerator()); }
#endregion
Ed ancora: l'interfaccia ICloneable prevede l'implementazione del
solo metodo Clone, che ritorna un duplicato del nostro
oggetto Shelf, con gli stessi valori di proprietà. Il codice in questo caso è il
seguente:
#region "Membri per ICloneable"
public object Clone()
{
Shelf ret = new Shelf(this._Name, this._Contents);
return (ret);
}
#endregion
Infine, l'interfaccia IList è un'ulteriore specializzazione di
ICollection e richiede i seguenti membri:
// Proprietà
public bool IsReadOnly
public bool IsFixedSize
// Metodi
public int Add(object WhichBook)
public bool Contains(object WhichBook)
public int IndexOf(object WhichBook)
public void Insert(int Position, object WhichBook)
public void Remove(object WhichBook)
public void RemoveAt(int Position)
public object this[int index]
Notare l'ultimo membro this, che ci permette di fare
riferimento ad un elemento inserito nello Shelf indicandolo,
per esempio, con una sintassi simile alla seguente:
Book mioLibro = (Book) mensola[1];
Ok, deriviamo, usiamo i generics e....via con
codice migliore!!!!
Notare il cast che ho fatto in questa istruzione. Notare anche che tutti i
membri delle interfacce che abbiamo esaminato hanno come parameter-type un
banale, semplice ed inutile object. Questo vuol dire casting continuo,
codice poco tipizzato, Intellisense poco utile e via dicendo: tutti quei
discorsi che abbiamo già fatto precedentemente trattando i generics. Acceleriamo
il discorso: potevamo ottenere lo stesso identico risultato derivando
direttamente dalla classe generica List<T>, creando una classe
Shelf implementata in questo modo:
class Shelf : List<Book>
{
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
// Costruttori
}
Adesso, la classe Shelf, con sole poche linee di codice,
implementa una proprietà Name che serve a noi per il nostro contesto, più tutti
i metodi previsti da ICollection e da IEnumerable. Pratico, veloce e comodo,
giusto? Questo rappresenta un utilizzo dei
generics: la cosa importante da far notare è che così facendo i metodi
Add, Contains, etc. non lavorano su poveri
object, ma direttamente su istanze di Book, con un grande vantaggio in termini
di.....ok, l'abbiamo già detto!