Condividi tramite


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 è diverso da un metodo ordinario nello stesso modo.

Esistono due chiavi per comprendere come la reflection gestisce i tipi e i metodi generici:

  • I parametri di tipo delle definizioni di tipo generico e delle definizioni di metodi generici sono rappresentati dalle istanze della Type classe .

    Annotazioni

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

  • Se un'istanza di Type rappresenta un tipo generico, include una matrice di tipi che rappresentano i parametri di tipo (per le definizioni di tipi generici) o gli argomenti di tipo (per i tipi costruiti). Lo stesso vale per un'istanza della MethodInfo classe che rappresenta un metodo generico.

Reflection fornisce metodi di Type e MethodInfo che consentono di accedere all'array di parametri di tipo e di determinare se un'istanza di Type rappresenta un parametro di tipo o un tipo effettivo.

Per un esempio di codice che illustra i metodi descritti qui, vedere Come esaminare e creare istanze di tipi generici con Reflection.

La discussione seguente presuppone la familiarità con la terminologia dei generics, ad esempio la differenza tra parametri di tipo e argomenti e tipi costruiti aperti o chiusi. Per altre informazioni, vedere Generics.

Si tratta di un tipo o di un metodo generico?

Quando si usa la reflection per esaminare un tipo sconosciuto, rappresentato da un'istanza di Type, utilizzare la IsGenericType proprietà per determinare se il tipo sconosciuto è generico. Restituisce true se il tipo è generico. Analogamente, quando si esamina un metodo sconosciuto, rappresentato da un'istanza della MethodInfo classe , utilizzare la IsGenericMethod proprietà per determinare se il metodo è generico.

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

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

Le definizioni di tipi e metodi generici sono i modelli da cui vengono creati 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 viene chiuso se sono stati sostituiti tipi istanziabili per tutti i relativi parametri di tipo, inclusi tutti i parametri di tipo di tutti i tipi contenitori. È possibile creare un'istanza di un tipo generico solo se è chiusa. La Type.ContainsGenericParameters proprietà restituisce true se un tipo è aperto. Per i metodi, il MethodBase.ContainsGenericParameters metodo esegue la stessa funzione.

Generare tipi generici chiusi

Dopo aver creato una definizione di tipo o metodo generico, usare il MakeGenericType metodo per creare un tipo generico chiuso o il MakeGenericMethod metodo per creare un MethodInfo oggetto per 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. È necessario avere una definizione di tipo o metodo generico. Utilizzare il GetGenericTypeDefinition metodo per ottenere la definizione di tipo generico o il GetGenericMethodDefinition metodo per ottenere la definizione del 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

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

Dopo aver appreso che un Type oggetto rappresenta un parametro di tipo, è possibile rispondere a molte domande aggiuntive. È possibile determinare l'origine del parametro di tipo, la relativa posizione e i relativi vincoli.

Parametro di tipo o argomento di tipo

Per determinare se un particolare elemento della matrice è un parametro di tipo o un argomento di tipo, utilizzare la IsGenericParameter proprietà . La IsGenericParameter proprietà è true se l'elemento è un parametro di tipo.

Un tipo generico può essere aperto senza essere una definizione di tipo generico, nel qual caso ha una combinazione di argomenti di tipo e parametri di tipo. Nel codice seguente, ad esempio, la classe D deriva da un tipo creato sostituendo il primo parametro di tipo di D per il secondo parametro di tipo di 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

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 che si sta esaminando, da un tipo contenitore o da un metodo generico. È possibile determinare l'origine del parametro di tipo generico come indicato di seguito:

  • Usare prima di tutto la DeclaringMethod proprietà 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, utilizzare la DeclaringType proprietà per determinare il tipo generico a cui appartiene il parametro di tipo generico.

Se il parametro di tipo appartiene a un metodo generico, la DeclaringType proprietà restituisce il tipo che ha dichiarato il metodo generico, che è irrilevante.

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 Type oggetto che rappresenta il B<int, V> tipo dell'esempio precedente. Il GetGenericArguments metodo fornisce un elenco di argomenti di tipo e, quando si esamina V, è possibile usare le proprietà DeclaringMethod e DeclaringType per individuare la provenienza. È quindi possibile utilizzare la proprietà per determinarne la GenericParameterPosition posizione nell'elenco dei parametri di tipo in cui è stata definita. In questo esempio, V si trova nella posizione 0 (zero) nell'elenco dei parametri di tipo in cui è stato definito.

Vincoli di tipo e interfaccia di base

Utilizzare il GetGenericParameterConstraints metodo 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 GenericParameterAttributes proprietà ottiene un GenericParameterAttributes valore 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 è covariante o controvariante, applicare la GenericParameterAttributes.VarianceMask maschera al GenericParameterAttributes valore restituito dalla GenericParameterAttributes proprietà . Se il risultato è GenericParameterAttributes.None, il parametro di tipo è invariante. Per altre informazioni, vedere Covarianza e Controvarianza.

Vincoli speciali

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

Invarianti

Per una tabella delle condizioni invarianti per i termini comuni in reflection per i tipi generici, vedere Type.IsGenericType. Per altri termini relativi ai metodi generici, vedere MethodBase.IsGenericMethod.