Condividi tramite


Generici in .NET

I generics consentono di personalizzare un metodo, una classe, una struttura o un'interfaccia in base al tipo di dati preciso su cui agisce. Ad esempio, anziché usare la classe Hashtable, che consente chiavi e valori di qualsiasi tipo, è possibile usare la classe generica Dictionary<TKey,TValue> e specificare i tipi consentiti per la chiave e il valore. Tra i vantaggi dei generici ci sono il riutilizzo del codice aumentato e la sicurezza dei tipi.

Definire e usare i generici

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 raccolta generica può usare un parametro di tipo come segnaposto per il tipo di oggetti archiviati. I parametri di tipo vengono visualizzati come tipi dei relativi campi e i tipi di parametro dei relativi metodi. Un metodo generico può usare il parametro di tipo come tipo del relativo valore restituito o come tipo di uno dei relativi parametri formali.

Il codice seguente illustra una semplice definizione di classe generica.

public class SimpleGenericClass<T>
{
    public T Field;
}
Public Class SimpleGenericClass(Of T)
    Public Field As T

End Class

Quando si crea un'istanza di una classe generica, si specificano i tipi effettivi da sostituire con i parametri di tipo. In questo modo viene stabilita una nuova classe generica, denominata classe generica costruita, con i tipi scelti sostituiti ovunque vengano visualizzati i parametri di tipo. Il risultato è una classe con controllo dei tipi adatta alla scelta dei tipi, come illustrato nel codice seguente.

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

Terminologia

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

  • Una definizione di tipo generico è una dichiarazione di classe, struttura o 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 è una definizione di tipo generico.

  • I parametri di tipo generico o i 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 di chiavi e valori.

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

  • Un argomento di tipo generico è qualsiasi tipo che viene sostituito a un parametro di tipo generico.

  • Il termine generico generico include sia tipi costruiti che definizioni di tipi generici.

  • 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 un tipo costruito di destinazione. La covarianza e la controvarianza sono collettivamente denominate varianza. Per altre informazioni, vedere Covarianza e controvarianza.

  • I vincoli sono limiti posti sui parametri di tipo generico. Ad esempio, è possibile limitare un parametro di tipo ai tipi che implementano l'interfaccia generica System.Collections.Generic.IComparer<T>, per assicurarsi che le istanze del tipo possano essere ordinate. È anche possibile vincolare i parametri di tipo ai tipi che dispongono di una determinata classe base, che dispongono di un costruttore senza parametri o che sono tipi riferimento o tipi valore. Gli utenti del 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 essere visualizzati come tipo restituito o come tipi di parametri formali, come illustrato nel codice seguente.

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

    I metodi generici possono essere visualizzati in tipi generici o non generici. È importante notare che un metodo non è generico solo perché appartiene a un tipo generico o anche perché ha parametri formali i cui tipi sono i parametri generici del tipo di inclusione. Un metodo è generico solo se ha un proprio elenco di parametri di tipo. Nel codice seguente, solo il metodo G è generico.

    class A
    {
        T G<T>(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    }
    class MyGenericClass<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 MyGenericClass(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

L'uso di raccolte e delegati generici offre molti vantaggi:

  • Sicurezza dei tipi. I generics spostano il carico di sicurezza dei tipi dall'utente al compilatore. Non è necessario scrivere codice per testare il tipo di dati corretto perché viene applicato in fase di compilazione. La necessità di conversione di tipi e la possibilità di errori di runtime sono ridotte al minimo.

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

    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Prestazioni migliori. I tipi di raccolta generici generalmente offrono prestazioni migliori per l'archiviazione e la manipolazione dei tipi valore perché non è necessario effettuare il boxing dei tipi valore.

  • I delegati generici consentono callback di tipo sicuro senza la necessità di creare più classi delegate. Ad esempio, il delegato generico Predicate<T> consente di creare un metodo che implementa criteri di ricerca personalizzati per un determinato tipo e di usare il metodo con metodi del tipo Array, ad esempio Find, FindLaste FindAll.

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

Di seguito sono riportate alcune limitazioni dei generics:

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

  • Le enumerazioni non possono avere parametri di tipo generico. Un'enumerazione può essere generica solo per caso ( ad esempio, perché è annidata in un tipo generico definito tramite 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 racchiuso in un tipo generico, a meno che i tipi non siano stati assegnati ai parametri di tipo di tutti i tipi che lo racchiudono. Un altro modo per dire questo è che in reflection, un tipo annidato definito usando questi linguaggi include i parametri di tipo di tutti i tipi che lo racchiudono. In questo modo i parametri di tipo dei tipi di inclusione possono essere usati nelle definizioni dei membri di un tipo annidato. Per altre informazioni, vedere "Tipi annidati" in MakeGenericType.

    Nota

    Non è necessario che un tipo annidato definito emettendo codice in un assembly dinamico o utilizzando l'IL assemblerIlasm.exe includa i parametri di tipo dei relativi tipi contenitori; tuttavia, se non li include, i parametri di tipo non rientrano nell'ambito nella classe annidata.

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

Supporto per librerie di classi e linguaggio

.NET offre una serie di classi generiche di raccolta nei seguenti namespace:

Le interfacce generiche per l'implementazione di confronti di ordinamento ed uguaglianza vengono fornite nello spazio dei nomi System, insieme ai tipi delegati generici per gestori eventi, conversioni e predicati di ricerca.

Lo spazio dei nomi System.Numerics fornisce interfacce generiche per le funzionalità matematiche (disponibili in .NET 7 e versioni successive). Per altre informazioni, vedere Matematica generica.

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

Il runtime di linguaggio comune fornisce nuovi codici operativi e prefissi per supportare i tipi generici nel common intermediate language (CIL), tra cui Stelem, Ldelem, Unbox_Any, Constrainede Readonly.

Visual C++, C# e Visual Basic offrono tutto il supporto completo per la definizione e l'uso di generics. Per altre informazioni sul supporto del linguaggio, vedere Tipi generici in Visual Basic, Introduzione ai generics e Panoramica dei generics in Visual C++.

Tipi annidati e generici

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

Titolo Descrizione
Raccolte generiche in .NET Vengono descritte le classi di raccolta generiche e altri tipi generici in .NET.
Delegati generici per la modifica di matrici ed elenchi Descrive i delegati generici per le conversioni, i predicati di ricerca e le azioni da eseguire sugli elementi di una matrice o di una raccolta.
Matematica generica Viene descritto come eseguire operazioni matematiche in modo generico.
Interfacce generiche Descrive le interfacce generiche che forniscono funzionalità comuni tra le famiglie di tipi generici.
Covarianza e controvarianza Descrive la covarianza e la controvarianza nei parametri di tipo generico.
Tipi di raccolta di uso comune Fornisce informazioni di riepilogo sulle caratteristiche e sugli 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 tipi di raccolta generici.
Procedura: Definire un tipo generico con Reflection Emit Viene illustrato come generare assembly dinamici che includono tipi e metodi generici.
Tipi generici in Visual Basic Descrive la funzionalità generics per gli utenti di Visual Basic, incluse le procedure per l'uso e la definizione di tipi generici.
Introduzione ai generics Offre una panoramica della definizione e dell'uso di tipi generici per gli utenti C#.
Panoramica dei generici in Visual C++ Descrive la funzionalità generics per gli utenti C++, incluse le differenze tra generics e modelli.

Riferimento