Generics in .NET

I generics consentono di personalizzare un metodo, una classe o una struttura in base ai dati precisi su cui interviene. Ad esempio, anziché usare la Hashtable classe, che consente chiavi e valori di qualsiasi tipo, è possibile usare la Dictionary<TKey,TValue> classe generica e specificare i tipi consentiti per la chiave e il valore. Tra i vantaggi dei generics ci sono una maggiore riutilizzabilità del codice e l'indipendenza dai tipi.

Definizione e utilizzo dei generics

I generics sono classi, strutture, interfacce e metodi dotati di segnaposto (parametri di tipo) per uno o più dei tipi archiviati o usati. Una classe di raccolte generiche può usare un parametro di tipo come segnaposto per il tipo di oggetti in essa contenuti. I parametri di tipo vengono visualizzati come i tipi dei relativi campi e i tipi di parametri dei relativi metodi. Un metodo generico potrebbe usare il parametro di tipo come il tipo di valore restituito o come il tipo di uno dei parametri formali. Nel codice seguente viene illustrata una definizione di classe generica semplice.

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

Quando si crea un'istanza di una classe generica, è possibile specificare i tipi effettivi da sostituire per i parametri di tipo. Ciò consente di stabilire una nuova classe generica, definita come una classe generica costruita, con tipi prescelti sostituiti a ogni occorrenza dei parametri di tipo. Il risultato è una classe indipendente dai tipi personalizzata in base alla scelta di tipi, come illustrato nel codice seguente.

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Terminologia dei generics

I termini seguenti vengono usati per discutere dei generics in .NET:

  • Una definizione di tipo generico è una classe, una struttura o una dichiarazione di interfaccia che funge da modello, con segnaposto per i tipi che può contenere o usare. Ad esempio, la classe System.Collections.Generic.Dictionary<TKey,TValue> può contenere due tipi: chiavi e valori. Poiché una definizione di tipo generico è solo un modello, non è possibile creare istanze di una classe, una struttura o un'interfaccia che sia una definizione di tipo generico.

  • Iparametri di tipo generico, o parametri di tipo, sono i segnaposto in una definizione di tipo o metodo generico. Il tipo generico System.Collections.Generic.Dictionary<TKey,TValue> ha due parametri di tipo, TKey e TValue, che rappresentano i tipi delle chiavi e dei valori relativi.

  • Un tipo generico costruito, o tipo costruito, è il risultato della specifica di tipi per i parametri di tipo generico di una definizione di tipo generico.

  • Un argomento di tipo generico è qualsiasi tipo che verrà sostituito con un parametro di tipo generico.

  • Il termine generale tipo generico include sia tipi costruiti sia definizioni di tipo generico.

  • La covarianza e la controvarianza dei parametri di tipo generico consentono di usare tipi generici costruiti i cui argomenti di tipo sono più derivati (covarianza) o meno derivati (controvarianza) rispetto a quelle di tipo costruito. La covarianza e la controvarianza sono definite collettivamente varianza. Per altre informazioni, vedere Covarianza e controvarianza.

  • Ivincoli sono limiti imposti su parametri di tipo generico. Ad esempio, è possibile limitare un parametro di tipo a tipi che implementano l'interfaccia generica System.Collections.Generic.IComparer<T> , per garantire la possibilità di ordinare le istanze del tipo. È anche possibile vincolare i parametri di tipo a tipi che hanno una determinata classe di base, con un costruttore senza parametri, o che siano tipi riferimento o tipi valore. Gli utenti di tipo generico non possono sostituire gli argomenti di tipo che non soddisfano i vincoli.

  • Una definizione di metodo generico è un metodo con due elenchi di parametri: un elenco di parametri di tipo generico e un elenco di parametri formali. I parametri di tipo possono apparire come tipo restituito o come tipi dei parametri formali, come illustrato nel codice seguente.

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

I metodi generici possono apparire su tipi generici o non generici. È importante tenere presente che un metodo si definisce non generico solo perché appartiene a un tipo generico o perché ha parametri formali i cui tipi sono i parametri generici del tipo contenitore. Un metodo è generico solo se ha un proprio elenco di parametri di tipo. Nel codice seguente, solo il metodo G è generico.

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

Vantaggi e svantaggi dei generics

Esistono numerosi vantaggi nell'utilizzo di delegati e raccolte di tipi generici:

  • Indipendenza dai tipi. I generics spostano il carico dell'indipendenza dai tipi al compilatore. Non è necessario scrivere codice per verificare il tipo di dati corretto perché viene applicato in fase di compilazione. La necessità del cast di tipo e la possibilità di errori di run-time sono ridotte.

  • Meno codice e il codice è più facilmente riutilizzato. Non è necessario ereditare da un tipo di base ed eseguire l'override di membri. Ad esempio, LinkedList<T> è pronto per l'uso immediato. Ad esempio, è possibile creare un elenco collegato di stringhe con la seguente dichiarazione di variabile:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Prestazioni migliori. I tipi di raccolte generiche offrono in genere prestazioni migliori per l'archiviazione e la modifica dei tipi di valore, perché non è necessario eseguirne il boxing.

  • I delegati generici consentono di eseguire il callback indipendente dai tipi senza la necessità di creare più classi delegate. Ad esempio, il delegato generico Predicate<T> consente di creare un metodo per implementare i propri criteri di ricerca di un tipo particolare e di usare il proprio metodo con metodi di tipo Array , ad esempio Find, FindLaste FindAll.

  • I generics ottimizzano il codice generato dinamicamente. Quando si usano i generics con il codice generato in modo dinamico non è necessario generare il tipo. In questo modo aumenta il numero di scenari in cui è possibile usare i metodi dinamici leggeri invece di generare interi assembly. Per altre informazioni, vedere Procedura: Definire ed eseguire metodi dinamici e DynamicMethod.

Di seguito sono elencate alcune limitazioni di generics:

  • I tipi generici possono derivare dalla maggior parte delle classi base, ad esempio MarshalByRefObject (e i vincoli possono essere usati per richiedere che i parametri di tipo generico derivino da classi base quali MarshalByRefObject). Tuttavia, .NET non supporta i tipi generici associati al contesto. Un tipo generico può derivare da ContextBoundObject, ma provare a creare un'istanza di quel tipo causa un TypeLoadException.

  • L'enumerazione non può avere parametri di tipo generico. Un'enumerazione può essere generica solo incidentalmente (ad esempio, perché è annidata in un tipo generico che viene definito con Visual Basic, C# o C++). Per altre informazioni, vedere "Enumerazioni" in Common Type System.

  • I metodi dinamici leggeri non possono essere generici.

  • In Visual Basic, C# e C++ non è possibile creare un'istanza di un tipo annidato in un tipo generico a meno che non siano stati assegnati i tipi ai parametri di tipo di tutti i tipi di inclusione. In altre parole, in una reflection un tipo annidato che viene definito con questi linguaggi contiene i parametri di tutti i relativi tipi di inclusione. Ciò consente di usare i parametri di tipo dei tipi in inclusione nelle definizioni di membro di un tipo annidato. Per altre informazioni, vedere "Tipi annidati" in MakeGenericType.

    Nota

    Un tipo annidato definito con la creazione di codice in un assembly dinamico o usando Ilasm.exe (Assembler IL) non deve includere i parametri di tipo dei relativi tipi di inclusione. Tuttavia, se non li include, i parametri di tipo non saranno nell'ambito della classe annidata.

    Per altre informazioni, vedere "Tipi annidati" in MakeGenericType.

Libreria di classi e supporto del linguaggio

.NET offre una serie di classi di raccolte generiche negli spazi dei nomi seguenti:

Nello spazio dei nomi System sono disponibili interfacce generiche per l'implementazione di confronti di uguaglianza e ordinamento, oltre ai tipi delegati generici per gestori eventi, conversioni e predicati di ricerca.

Il supporto dei generics è stato aggiunto allo spazio dei nomi System.Reflection per l'esame di tipi e metodi generici, a System.Reflection.Emit per la creazione di assembly dinamici che contengono tipi e metodi generici e a System.CodeDom per la generazione di grafici di origine che includono generics.

Il Common Language Runtime fornisce nuovi codici operativi e prefissi per supportare i tipi generici nel linguaggio MSIL (Microsoft Intermediate Language), inclusi Stelem, Ldelem, Unbox_Any, Constrainede Readonly.

Visual C++, C# e Visual Basic forniscono il supporto completo per la definizione e l'utilizzo dei generics. Per altre informazioni sul supporto dei linguaggi, vedere Tipi generici in Visual Basic, Introduzione ai generics e Panoramica dei generics in C++.

Generics e tipi annidati

Un tipo annidato all'interno di un tipo generico può dipendere dai parametri di tipo del tipo generico che lo contiene. Common Language Runtime considera i tipi annidati come generici, anche se non dispongono di propri parametri di tipo generico. Quando si crea un'istanza di un tipo annidato, è necessario specificare gli argomenti di tipo per tutti i tipi generici.

Titolo Descrizione
Raccolte generiche in .NET Vengono descritte le classi di raccolte generiche e altri tipi generici in .NET.
Delegati generici per la modifica di matrici e elenchi Vengono descritti i delegati generici per conversioni, predicati di ricerca e azioni da eseguire sugli elementi di una matrice o raccolta.
Interfacce generiche Vengono descritte le interfacce generiche che forniscono funzionalità comuni a famiglie di tipi generici.
Covarianza e contravarianza Vengono descritte la covarianza e controvarianza nei parametri di tipo generico.
Tipi di raccolta comunemente usati Fornisce informazioni di riepilogo sulle caratteristiche e gli scenari di utilizzo dei tipi di raccolta in .NET, inclusi i tipi generici.
Quando usare raccolte generiche Vengono descritte le regole generali per determinare quando usare i tipi di raccolte generiche.
Procedura: Definire un tipo generico tramite reflection emit Viene illustrato come generare assembly dinamici che includono tipi e metodi generici.
Generic Types in Visual Basic Viene descritta la funzionalità generics per gli utenti di Visual Basic, tra cui procedure relative all'utilizzo e alla definizione di tipi generici.
Introduzione a Generics Viene fornita una panoramica di definizione e utilizzo di tipi generici per gli utenti di C#.
Panoramica di Generics in Visual C++ Viene descritta la funzionalità generics per gli utenti di C++, incluse le differenze tra generics e modelli.

Riferimento

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes