Cerca nel sito
Venerdi, 29 Luglio 2016
Ultimo aggiornamento: Mercoledi, 2 Settembre 2009
Home Articoli Progetti Gallerie
icona articolo

I generics

Per generics intendiamo una classe, una struttura, un’interfaccia o un metodo nel quale uno o più tipi effettivi vengono sostituiti da parametri al fine da assicurare al codice che si sviluppa una maggiore flessibilità ed indipendenza dai tipi.

I generics quindi consentono lo sviluppo di elementi di programma che svolgono una o più funzioni indipendentemente dai tipi sui quali agiscono e vengono generalmente utilizzati per creare classi di insieme.

Una lista dinamica ad esempio può essere intesa come un contenitore di oggetti generici (ovvero di qualsiasi tipo) ed il comportamento dei metodi di inserimento ed eliminazione di oggetti dalla lista è assolutamente indipendente da quello che è il tipo degli oggetti considerati: in questo caso l’impiego dei tipi generici e dei parametri ci permette di evitare lo sviluppo di una lista per ogni tipo di oggetto che vogliamo trattare ma di crearne una che sia utilizzabile da tutti i tipi.

Naturalmente quando si crea un’istanza di una classe generica occorre specificare i tipi effettivi sui quali questa agisce: tali tipi prenderanno il posto dei parametri di tipo definiti all’interno della classe stessa.

Per parametri di tipo intendiamo i segnaposto utilizzati nella definizione di un tipo o di un metodo al posto dei tipi effettivi per creare un tipo generico.

Così, utilizzando la notazione del c#, la definizione di una classegenerica potrebbe essere del tipo:

public class Generica
{
   public T campo;

   ...

}


in tal modo viene definita una classe generica che utilizza il segnaposto T al posto del tipo effettivo.

Questo significa che nel momento in cui verrà creata un’istanza della classe Generica occorrerà indicare, diversamente da quanto accade tradizionalmente, anche il tipo effettivo corrispondente al segnaposto T.

Se ad esempio si vuole creare un’istanza della classe Generica in cui il tipo del membro della classe campo sia una stringa si dovrà scrivere:

Generica g = new Generica();


se ad esempio si vuole creare un’istanza in cui tipo del membro campo sia un intero si dovrà utilizzare il seguente codice:

Generica g = new Generica();


Una volta definita l’istanza questa può essere trattata come se fosse un’istanza della classe non generica ottenuta sostituendo il parametro di tipo con il tipo effettivo.

Per tornare ai nostri esempi è come se con la scrittura:

Generica g = new Generica();


si creasse un’istanza della classe:

public class Generica
{
   public Stringa campo;

   ...
}



icona articolo

Parametri di tipo



Per convenzione nella definizione di un tipo generico si utilizzano dei parametri di tipo il cui nome deve:
  • essere descrittivo, a meno che un nome composto da una singola lettera non pregiudichi la facile comprensione del codice
  • iniziare con la lettera T

public T Somma(T a, T b);


E’ possibile altresì applicare delle restrizioni ai tipi che possono essere utilizzati al momento della creazione di un’istanza di un tipo generico: se si tenta di creare un’istanza di un tipo generico utilizzanto un tipo effettivo non compatibile con quelli che sono i vincoli definiti viene generato un errore in fase di compilazione.

Esistono sei tupi di vincoli tutti applicabili mediante la parola chiave where:
  • "where T: struct" indica che l’argomento di tipo deve essere un tipo valore
  • "where T: class" indica che l’argomento di tipo deve essere un tipo riferimento
  • "where T: new()" indica che l’argomento di tipo deve disporre di un costruttore publico privo di parametro: se utilizzato insieme ad altri vincoli deve essere indicato per ultimo.
  • "where T: nome_classe" indica che l’argomento di tipo deve corrispondere alla classe indicata oppure derivare da essa
  • "where T: nome_interfaccia" indica che l’argomento di tipo deve corrispondere all’interfaccia indicata oppure implementarla
  • "where T: U" indica che l’argomento di tipo fornito per T deve corrispondere a quello fornito per U: si tratta di un vincolo di tipo naked.

E’ bene precisare che su un parametro di tipo è possibile applicare più vincoli separandoli tramite virgola; sui parametri di tipo non vincolati non è possibile utilizzare gli operatori == e != in quanto potrebbero non essere supportati.


icona articolo

Classi generiche



Nella progettazione di un’interfaccia generica occorre prestare attenzione a due aspetti:
  • quali sono i tipi da generalizzare in parametri di tipo
  • quali sono i vincoli da applicare ai parametri di tipo

Tipicamente nella creazione di una classe generica si parte da una classe concreta esistente e si modificano di volta in volta i tipi effettivi in parametri di tipo fino a raggiungere un compromesso fra generalizzazione e possibilità di utilizzo.

public class Stack< T >
{
   T[] elements;
   int count;

   public void Push(T item)
   {
      ...
   }

   public T Pop()
   {
      ...
   }
}



icona articolo

Metodi e delegati generici



L’utilizzo dei tipi generici può interessare anche la definizione di metodi e delegati generici: il funzionamento è del tutto analogo a quanto visto per la definizione di classi generiche:
  • si definiscono i parametri di tipo in fase di implementazione del metodo (sia come tipo restituito sia come argomenti del metodo) o del delegato
  • in fase di creazione di instanza si specifica il tipo effettivo da utilizzare

public void Swap(ref T a, ref T b)
{
   T temp;
   temp = a;
   a = b;
   b = temp;
}


public delegate void DelegatoGenerico(object oggetto, T datievento);