Reflection e tipi generici

Dal punto di vista della reflection, la differenza tra un tipo generico e un tipo comune è che a un tipo generico è associato un set di parametri di tipo (se si tratta di una definizione di tipo generico) o argomenti di tipo (se si tratta di un tipo costruito). Un metodo generico si differenzia da un metodo ordinario esattamente nello stesso modo.

Per comprendere come i tipi e i metodi generici vengono gestiti dalla reflection, è opportuno considerare due fattori fondamentali:

  • I parametri di tipo delle definizioni di tipo e di metodo generico sono rappresentati da istanze della classe Type .

    Nota

    Numerosi metodi e proprietà di Type hanno un comportamento diverso quando un oggetto Type rappresenta un parametro di tipo generico. Queste differenze sono documentate negli articoli sulle proprietà e sui metodi. Vedere ad esempio IsAutoClass e DeclaringType. Inoltre, alcuni membri sono validi solo quando un oggetto Type rappresenta un parametro di tipo generico. Ad esempio, vedere GetGenericTypeDefinition.

  • Se un'istanza di Type rappresenta un tipo generico, includerà una matrice di tipi che rappresentano i parametri di tipo (per le definizioni di tipo generico) o gli argomenti di tipo (per i tipi costruiti). Questo è vero anche per un'istanza della classe MethodInfo che rappresenta un metodo generico.

La reflection fornisce metodi di Type e MethodInfo che consentono di accedere alla matrice di parametri di tipo e stabilire se un'istanza di Type rappresenta un parametro di tipo o un tipo effettivo.

Per un codice di esempio in cui sono illustrati i metodi indicati in questo argomento, vedere How to: Examine and Instantiate Generic Types with Reflection (Procedura: Esaminare e creare istanze di tipi generici con reflection).

Nelle considerazioni che seguono si presuppone la conoscenza della terminologia relativa ai generics, ad esempio la differenza tra argomenti e parametri di tipo e quella tra tipi costruiti aperti o chiusi. Per altre informazioni, vedere Generics.

Si tratta di un tipo o di un metodo generico?

Quando si esamina un tipo sconosciuto rappresentato da un'istanza di Typetramite la reflection, usare la proprietà IsGenericType per stabilire se il tipo sconosciuto è generico. Se il tipo è generico, restituisce true . In modo analogo, quando si esamina un metodo sconosciuto rappresentato da un'istanza della classe MethodInfo , usare la proprietà IsGenericMethod per stabilire se il metodo è generico.

Si tratta di una definizione di metodo o di tipo generico?

Usare la proprietà IsGenericTypeDefinition per stabilire se un oggetto Type rappresenta una definizione di tipo generico e il metodo IsGenericMethodDefinition per determinare se un oggetto MethodInfo rappresenta una definizione di metodo generico.

Le definizioni di metodo e di tipo generico costituiscono i modelli a partire dai quali vengono creati i tipi istanziabili. I tipi generici nelle librerie .NET, ad esempio Dictionary<TKey,TValue>, sono definizioni di tipi generici.

Il tipo o il metodo è aperto o chiuso?

Un tipo o un metodo generico è chiuso se tutti i relativi parametri di tipo, inclusi tutti i parametri di tipo di tutti i tipi di inclusione, sono stati sostituiti da tipi istanziabili. È possibile creare un'istanza di un tipo generico solo se è chiusa. Se un tipo è aperto, la proprietà Type.ContainsGenericParameters restituisce true . Per i metodi, la stessa funzione viene eseguita dal metodo MethodBase.ContainsGenericParameters .

Generare tipi generici chiusi

Dopo avere ottenuto una definizione di metodo o di tipo generico, usare il metodo MakeGenericType per creare un tipo generico chiuso oppure il metodo MakeGenericMethod per creare un oggetto MethodInfo relativo a un metodo generico chiuso.

Ottenere la definizione del tipo o del metodo generico

Se si dispone di un tipo o un metodo generico aperto che non è una definizione di tipo o metodo generico, non è possibile crearne istanze e non è possibile specificare i parametri di tipo mancanti. A questo scopo è necessario avere una definizione di tipo o di metodo generico. Usare il metodo GetGenericTypeDefinition per ottenere la definizione di tipo generico o il metodo GetGenericMethodDefinition per ottenere la definizione di metodo generico.

Ad esempio, se si dispone di un Type oggetto che rappresenta Dictionary<int, string> e si desidera creare il tipo Dictionary<string, MyClass>, è possibile utilizzare il GetGenericTypeDefinition metodo per ottenere un Type oggetto che rappresenta Dictionary<TKey, TValue> e quindi utilizzare il MakeGenericType metodo per produrre un Type oggetto che rappresenta Dictionary<int, MyClass>.

Per un esempio di tipo generico aperto che non è un tipo generico, vedere Parametro di tipo o argomento di tipo.

Esaminare gli argomenti di tipo e i parametri di tipo

Usare il metodo Type.GetGenericArguments per ottenere una matrice di oggetti Type che rappresentano i parametri o gli argomenti di tipo di un tipo generico e il metodo MethodInfo.GetGenericArguments per eseguire la stessa operazione per un metodo generico.

Dopo avere stabilito che l'oggetto Type rappresenta un parametro di tipo, la reflection consentirà di ottenere altre informazioni al riguardo. È possibile determinare l'origine, la posizione e i vincoli del parametro di tipo.

Parametro di tipo o argomento di tipo

Per stabilire se un determinato elemento della matrice è un parametro di tipo oppure un argomento di tipo, usare la proprietà IsGenericParameter . Se l'elemento è un parametro di tipo, la proprietà IsGenericParameter restituisce true .

Un tipo generico può essere aperto senza essere una definizione di tipo generico, nel qual caso presenterà sia argomenti di tipo che parametri di tipo. Ad esempio, nel codice seguente la classe D deriva da un tipo creato sostituendo il primo parametro di tipo D al secondo parametro di tipo B.

class B<T, U> {}
class D<V, W> : B<int, V> {}
Class B(Of T, U)
End Class
Class D(Of V, W)
    Inherits B(Of Integer, V)
End Class
generic<typename T, typename U> ref class B {};
generic<typename V, typename W> ref class D : B<int, V> {};

Se si ottiene un Type oggetto che rappresenta D<V, W> e si usa la proprietà per ottenere il BaseType tipo di base, il risultato type B<int, V> è aperto, ma non è una definizione di tipo generico.

Origine di un parametro generico

Un parametro di tipo generico può provenire dal tipo in esame, da un tipo di inclusione o da un metodo generico. È possibile determinarne l'origine nel modo seguente:

  • Prima di tutto, usare la proprietà DeclaringMethod per determinare se il parametro di tipo proviene da un metodo generico. Se il valore della proprietà non è un riferimento Null, l'origine è un metodo generico.
  • Se l'origine non è un metodo generico, usare la proprietà DeclaringType per determinare il tipo generico a cui appartiene il parametro di tipo generico.

Se il parametro di tipo appartiene a un metodo generico, la proprietà DeclaringType restituisce il tipo che ha dichiarato tale metodo, che non è un'informazione rilevante.

Posizione di un parametro generico

In rari casi, è necessario determinare la posizione di un parametro di tipo nell'elenco dei parametri di tipo della relativa classe dichiarante. Si supponga ad esempio di avere un oggetto Type che rappresenta il tipo B<int, V> dell'esempio precedente. Il metodo GetGenericArguments fornisce un elenco di argomenti di tipo ed è possibile determinare l'origine del parametro V in esame usando le proprietà DeclaringMethod e DeclaringType . È quindi possibile usare la proprietà GenericParameterPosition per determinare la posizione del parametro nell'elenco dei parametri del tipo in cui il parametro è stato definito. In questo esempio, V si trova nella posizione 0 (zero) dell'elenco di parametri di tipo in cui è stato definito.

Vincoli di tipo e interfaccia di base

Usare il metodo GetGenericParameterConstraints per ottenere il vincolo del tipo di base e i vincoli di interfaccia di un parametro di tipo. L'ordine degli elementi della matrice non è significativo. Un elemento rappresenta un vincolo di interfaccia se si tratta di un tipo di interfaccia.

Attributi dei parametri generici

La proprietà GenericParameterAttributes ottiene un valore GenericParameterAttributes che indica la varianza (covarianza o controvarianza) e i vincoli speciali di un parametro di tipo.

Covarianza e controvarianza

Per determinare se un parametro di tipo sia covariante o controvariante, applicare la maschera GenericParameterAttributes.VarianceMask al valore GenericParameterAttributes restituito dalla proprietà GenericParameterAttributes . Se il risultato è GenericParameterAttributes.None, il parametro di tipo è invariante. Per altre informazioni, vedere Covarianza e controvarianza.

Vincoli speciali

Per stabilire i vincoli speciali di un parametro di tipo, applicare la maschera GenericParameterAttributes.SpecialConstraintMask al valore GenericParameterAttributes restituito dalla proprietà GenericParameterAttributes . Se il risultato è GenericParameterAttributes.None, non sono presenti vincoli speciali. Un parametro di tipo può essere vincolato per essere un tipo riferimento, un tipo valore non nullable e per avere un costruttore senza parametri.

Invarianti

Per una tabella di condizioni non variabili associate a termini comuni nella reflection per tipi generici, vedere Type.IsGenericType. Per altri termini correlati ai metodi generici, vedere MethodBase.IsGenericMethod.