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.

generic<typename T>
public ref class SimpleGenericClass
{
public:
    T Field;
};
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.

static void Main()
{
    SimpleGenericClass<String^>^ g = gcnew SimpleGenericClass<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("SimpleGenericClass.Field           = \"{0}\"", g->Field);
    Console::WriteLine("SimpleGenericClass.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
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 genericoo i parametri di tiposono 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 generale tipo generico include sia tipi costruiti che definizioni di tipi generici.

  • Covarianza e controvarianza dei parametri di tipo generico consentono di utilizzare 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 vengono collettivamente definite 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.

  • Un 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.

    generic<typename T>
    T MyGenericMethod(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
    
    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.

    ref class A
    {
        generic<typename T>
        T G(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    };
    generic<typename T>
    ref class MyGenericClass
    {
        T M(T arg)
        {
            T temp = arg;
            //...
            return temp;
        }
    };
    
    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 dei tipi e la possibilità di errori in fase di esecuzione sono ridotte.

  • 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 = gcnew LinkedList<String^>();
    
    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

    Un tipo annidato definito emettendo codice in un assembly dinamico o utilizzando il Ilasm.exe (Assembler IL) non è necessario includere i parametri di tipo dei tipi circostanti; tuttavia, qualora non li includa, i parametri di tipo non sono disponibili 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 ulteriori 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, Introduction to Genericse Overview of 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 manipolazione di array e liste 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.
Operazioni matematiche generiche 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 le 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