Tutorial
Implementare le collezioni con C#


Data: Agosto-2002
Autore: (luKa)
Ambiente: .NET Framework v1.0

Download Esempi

Introduzione

Il .NET Framework dispone di un'ampia varietà di collezioni eppure esistono situazioni in cui è conveniente implementarsi una propria collezione, questo tutorial guida all'implementazione consapevole di collezioni personalizzate.
L'implementazione di nuove collezioni viene descritto in modo estremamente pratico: il tutorial è da leggere tenendo il codice degli esempi citati costantemente a portata di mano per poterlo ispezionare ed eseguire passo-passo.
Ciò a cui punta il tutorial è spiegare il perché lasciando alla documentazione di riferimento ufficiale approfondire il come.

Una volta letto il tutorial e compreso gli esempi si sarà in grado di determinare quando è conveniente implemetare una propria collezione, scegliere la tecnica di implementazione adatta e quindi procedere all'implementazione.

Perché implementare una collezione

Il .NET Framework mette a disposizione collezioni, che fanno uso di algoritmi efficienti, con una varietà tale da soddisfare le esigenze di chi privilegia tempi di accesso ottimi, di chi privilegia tempi di inserimento/modifica ottimi e di chi privilegia il minimo consumo di memoria.
Ma se il .Net Framework è la panacea per tutti i nostri mali, perché spendere tempo ed energie per implementare altre collezioni???
Ci sono due ragioni per cui uno sviluppatore può desiderare di implementare una sua collezione:

Implementare una collezione con l'ereditarietà

Un modo per implementare una collezione personalizzata in C# è quello di sfruttare l'ereditarietà a partire da classi base opportunamente fornite dal Framework nel namespace System.Collections e System.Collections.Specialized. Le classi base astratte forniscono già l'implementazione di tutti i metodi necessari, mentre per i metodi di tali classi ecco la casistica:
La collezione così implementata potrà essere fortemente tipizzata e anche fornire ogni altro servizio desiderato, per esempio la persistenza, la ricerca con condizioni di filtro, il vincolo a specifiche regole di Business, etc.
Segue una lista delle classi base e relativi ambiti di utilizzo:
Si vedano i seguenti esempi:
COLLECTIONBASE.CS che eredita da CollectionBase.
   E' disponibile un'utility per generare automaticamente il codice:  CollGen.zip per la Beta2 e CollGen.zip per la Rel 1 (in locale)
- READONLYCB.CS che eredita da ReadOnlyCollectionBase.
- DICTIONARYBASE.CS che eredita da DictionaryBase.

Implementare una collezione con il contenimento

Un altro metodo per implementare una collezione consiste nel contenimento ossi implementare autonomamente tutti i metodi necessari alla collezione (senza ereditarli) utilizzando al proprio interno le strutture dati utili a facilitare tale collezione. Per esempio un Array tipizzato o una collezione non tipizzata tra quelle fornite dal Framework.

I metodi da implementare sono determinati dalle interfacce che una collezione deve necessariamente implementare per essere una collezione. In particolare tutte le collezioni devono implementare l'interfaccia IEnumerable che può essere estesa da ICollection. Si distinguono inoltre i due gruppi di collezioni che implementano due interfacce derivate da ICollection: IDictionary e IList .

Gerarchia delle classi


Per supportare la creazione di collezioni per contenimento è disponibile l'Add-In di VS.NET open-source .NET CollectionGen.
Notizie sull'Add-In sono disponibili in:
    http://www.sellsbrothers.com/tools/default.aspx  
È possibile scaricare l'Add-In da:
    http://www.sellsbrothers.com/tools/collectiongen.zip   (in locale)

A proposito delle collezioni implementate per contenimento si vedano gli esempi:
- INNERARRAY.CS mostra l'implemetazione fornita dall'Add-In di una collezione di tipo IList con un Array tipizzato.
- INNERHASH.CS mostra l'implemetazione fornita dall'Add-In di una collezione di tipo IDictionary con una HashTable.

Scegliere tra le due tecniche di implementazione

Nella maggior parte delle situazioni l'implementazione per ereditarietà è sufficiente, vi sono comunque situazioni in cui è necessaria l'implementazione per contenimento.

In generale la relazione di l'ereditarietà è preferibile per relazioni strutturali e immutabili nel tempo del tipo Is-A. Mentre una relazione di contenimento (che può nascondere il tipo contenuto) è preferibile per relazioni non strutturali del tipo Has-A e quindi è più flessibile. Segue un breve confronto tra le due tecniche applicate all'implementazione di collezioni:
Sintetizzando si ottiene questo albero di decisione:
  1. Se si deve implementare una collezione di Value-Type (structure comprese) in cui le prestazioni sono determinanti usale il contenimento
  2. altrimenti... se è necessario implementarte due o più collezioni nella medesima classe usare il contenimento 
  3. altrimenti... usare l'ereditarietà
Nel dubbio... usare l'ereditarietà.