Area di riferimento
- Developing applications that use system types and collections
- Manage data in a .NET Framework application by using the .NET Framework 2.0 system types
- Generic types
Generic Types Constraints
Quando viene compilato del codice generico, il compilatore lo analizza in modo da assicurare che il codice funzionerà per qualsiasi tipo esistente o che potrebbe essere definito in futuro. Questo penalizza molto il programmatore in quanto è costretto a utilizzare soltato i metodi della classe Object per tutti gli argomenti generici . Di fatto questo renderebbe i generics praticamente inutili.
Per superare questa limitazione il CLR supporta i constraints che permettono di limitare il numero di tipi che possono essere specificati come argomenti generici. Ciò permette al programmatore di compiere maggiori operazioni su questi tipi.
Vediamo un esempio di funzione generica che calcola il minimo tra due oggetti:
public static T min<T>(T a, T b) where T : IComparable<T>
{
if (a.CompareTo(b) <= 0)
{
return a;
}
return b;
}
La parola chiave where di C# permette di definire i constraints. In questo caso si limita l'utilizzo della funzione soltato ai tipi che implementano l'interfaccia IComparable<T>. Ciò permette allo sviluppatore di utilizzare il metodo CompareTo.
Il CLR permette di applicare tre tipi di constraints: primary constraint, secondary constraint e/o constructor constraint.
Per ogni parametro di tipo è possibile specificare al massimo un primary constraint che specifica se esso è una classe (class) o un value-type (struct) oppure se appartiene ad uno specifico tipo.
public class Classe<T> where T : class
{
// T deve essere un reference-type
}
public class Classe<T> where T : struct
{
// T deve essere un value-type
}
public class Classe<T> where T : Stream
{
// T deve appartenere a un tipo derivato da Stream
}
Per ogni parametro di tipo è possibile specificare zero o più secondary constraints che specificano una interfaccia che deve essere implementata ( vedi esempio min<T> ). Un altro tipo di secondary constraint chiamato type parameter constraint permette di specificare che tra due argomenti di tipo ci sia una relazione.
public class Classe<T, TBase> where T : TBase
{
// T deve essere un tipo derivato da TBase
}
Infine un constructor constraint permette di garantire che l'argomento di tipo possieda un costruttore senza parametri. Ciò offre al programmatore la possibilità di istanziare oggetti generici all'interno del codice.
public static class Classe<T> where T : new()
{
// T deve essere un tipo derivato da TBase
public static T getTipo()
{
return new T();
}
}
E' importante sottolineare la possibilità di utilizzare la parola chiave default per impostare una variabile generica al suo valore di default. Il JIT compiler del CLR sostituirà questo statement con un assegnamento a null per un reference-type e impostando tutti i bit a zero per un value-type.
public class Default<T>
{
T temp = default(T); // assume il valore di default del tipo T
}