Réflexion et types génériques

Du point de vue de la réflexion, la différence entre un type générique et un type ordinaire est qu’un type générique est associé à un ensemble de paramètres de type (s’il s’agit d’une définition de type générique) ou d’arguments de type (s’il s’agit d’un type construit). Une méthode générique diffère d'une méthode ordinaire de la même façon.

Deux éléments essentiels permettent de comprendre comment la réflexion gère les types et méthodes génériques :

  • Les paramètres de type des définitions de type générique et des définitions de méthode générique sont représentés par des instances de la classe Type .

    Notes

    De nombreuses propriétés et méthodes de Type ont un comportement différent quand un objet Type représente un paramètre de type générique. Ces différences sont documentées dans les articles de la propriété et de la méthode. Par exemple, consultez IsAutoClass et DeclaringType. Par ailleurs, certains membres sont valides uniquement quand un objet Type représente un paramètre de type générique. Par exemple, consultez GetGenericTypeDefinition.

  • Si une instance de Type représente un type générique, elle inclut un tableau de types qui représentent les paramètres de type (pour les définitions de type générique) ou les arguments de type (pour les types construits). Il en est de même d'une instance de la classe MethodInfo qui représente une méthode générique.

La réflexion fournit des méthodes de Type et MethodInfo qui permettent d'accéder au tableau des paramètres de type et de déterminer si une instance de Type représente un paramètre de type ou un type réel.

Pour obtenir un exemple de code illustrant les méthodes présentées ici, consultez Guide pratique pour examiner et instancier des types génériques avec la réflexion.

La discussion suivante suppose que vous connaissez la terminologie des génériques, par exemple, la différence entre les paramètres et les arguments de type et entre les types construits ouverts ou fermés. Pour plus d’informations, consultez Génériques.

S’agit-il d’un type ou d’une méthode générique ?

Quand vous utilisez la réflexion pour examiner un type inconnu, représenté par une instance de Type, utilisez la propriété IsGenericType pour déterminer si le type inconnu est générique. Elle retourne true si le type est générique. De la même façon, quand vous examiner une méthode inconnue, représentée par une instance de la classe MethodInfo , utilisez la propriété IsGenericMethod pour déterminer si la méthode est générique.

S’agit-il d’une définition de type ou de méthode générique ?

Utilisez la propriété IsGenericTypeDefinition pour déterminer si un objet Type représente une définition de type générique et utilisez la méthode IsGenericMethodDefinition pour déterminer si un MethodInfo représente une définition de méthode générique.

Les définitions de type et de méthode génériques sont les modèles à partir desquels les types instanciables sont créés. Les types génériques dans les bibliothèques .NET, tels que Dictionary<TKey,TValue>, sont des définitions de types génériques.

Le type ou la méthode est-il ouvert ou fermé ?

Un type ou une méthode générique est fermé si des types instanciables ont été substitués à tous ses paramètres de type, y compris tous les paramètres de type de tous les types englobants. Vous pouvez uniquement créer une instance d’un type générique s’il est fermé. La propriété Type.ContainsGenericParameters renvoie true si un type est ouvert. Pour les méthodes, la méthode MethodBase.ContainsGenericParameters a la même fonction.

Générer des types génériques fermés

Une fois que vous avez une définition de type ou méthode générique, utilisez la méthode MakeGenericType pour créer un type générique fermé ou la méthode MakeGenericMethod pour créer un MethodInfo pour une méthode générique fermée.

Obtenir la définition de type ou de méthode générique

Si vous avez un type ou une méthode générique ouvert qui n’est pas une définition de type ou de méthode générique, vous ne pouvez pas créer d’instances de ce type et vous ne pouvez pas fournir les paramètres de type manquants. Vous devez avoir une définition de type ou de méthode générique. Utilisez la méthode GetGenericTypeDefinition pour obtenir la définition de type générique ou la méthode GetGenericMethodDefinition pour obtenir la définition de méthode générique.

Par exemple, si vous avez un objet Type représentant Dictionary<int, string> et que vous voulez créer le type Dictionary<string, MyClass>, vous pouvez utiliser la méthode GetGenericTypeDefinition pour obtenir un Type représentant Dictionary<TKey, TValue>, puis utiliser la méthode MakeGenericType pour produire un Type représentant Dictionary<int, MyClass>.

Pour obtenir un exemple de type générique ouvert qui n’est pas un type générique, consultez Paramètre de type ou argument de type.

Examiner les arguments de type et les paramètres de type

Utilisez la méthode Type.GetGenericArguments pour obtenir un tableau d'objets Type qui représentent les paramètres ou les arguments de type d'un type générique, et utilisez la méthode MethodInfo.GetGenericArguments pour faire de même pour une méthode générique.

Dès que vous savez qu'un objet Type représente un paramètre de type, la réflexion peut répondre à de nombreuses autres questions. Vous pouvez déterminer la source du paramètre de type, sa position et ses contraintes.

Paramètre de type ou argument de type

Pour déterminer si un élément particulier du tableau est un paramètre ou un argument de type, utilisez la propriété IsGenericParameter . La propriété IsGenericParameter a la valeur true si l'élément est un paramètre de type.

Un type générique peut être ouvert sans être une définition de type générique, dans ce cas, il comprend un mélange d'arguments de type et de paramètres de type. Par exemple, dans le code suivant, la classe D dérive d'un type créé en substituant le premier paramètre de type D au deuxième paramètre de type 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> {};

Si vous obtenez un objet Type représentant D<V, W> et utilisez la propriété BaseType pour obtenir son type de base, le type B<int, V> résultant est ouvert, mais n’est pas une définition de type générique.

Source d’un paramètre générique

Un paramètre de type générique peut provenir du type que vous examinez, d'un type englobant ou d'une méthode générique. Vous pouvez déterminer la source du paramètre de type générique comme suit :

  • Tout d'abord, utilisez la propriété DeclaringMethod pour déterminer si le paramètre de type vient d'une méthode générique. Si la valeur de propriété n’est pas une référence null, la source est une méthode générique.
  • Si la source n'est pas une méthode générique, utilisez la propriété DeclaringType pour déterminer le type générique auquel appartient le paramètre de type générique.

Si le paramètre de type appartient à une méthode générique, la propriété DeclaringType retourne le type qui a déclaré la méthode générique, ce qui n'est pas pertinent.

Position d’un paramètre générique

Dans de rares cas, il est nécessaire de déterminer la position d’un paramètre de type dans la liste de paramètres de type de la classe de déclaration. Par exemple, supposons que vous ayez un objet Type représentant le type B<int, V> de l'exemple précédent. La méthode GetGenericArguments vous donne la liste des arguments de type et, quand vous examinez V , vous pouvez utiliser les propriétés DeclaringMethod et DeclaringType pour découvrir d'où il vient. Vous pouvez ensuite utiliser la propriété GenericParameterPosition afin de déterminer sa position dans la liste de paramètres de type dans laquelle il a été défini. Dans cet exemple, V se trouve à la position 0 (zéro) dans la liste de paramètres de type dans laquelle il a été défini.

Type de base et contraintes d’interface

Utilisez la méthode GetGenericParameterConstraints pour obtenir la contrainte de type de base et les contraintes d'interface d'un paramètre de type. L'ordre des éléments du tableau n'est pas significatif. Un élément représente une contrainte d’interface s’il s’agit d’un type d’interface.

Attributs de paramètre générique

La propriété GenericParameterAttributes obtient une valeur GenericParameterAttributes qui indique l'écart (covariance ou contravariance) et les contraintes spéciales d'un paramètre de type.

Covariance et contravariance

Pour déterminer si un paramètre de type est covariant ou contravariant, appliquez le masque GenericParameterAttributes.VarianceMask à la valeur GenericParameterAttributes retournée par la propriété GenericParameterAttributes . Si le résultat est GenericParameterAttributes.None, le paramètre de type est invariant. Pour plus d’informations, consultez Covariance et contravariance.

Contraintes spéciales

Pour déterminer les contraintes spéciales d'un paramètre de type, appliquez le masque GenericParameterAttributes.SpecialConstraintMask à la valeur GenericParameterAttributes retournée par la propriété GenericParameterAttributes . Si le résultat est GenericParameterAttributes.None, il n'y a aucune contrainte spéciale. Un paramètre de type peut être contraint à être un type de référence, un type de valeur qui n'autorise pas les valeurs null et à avoir un constructeur sans paramètre.

Invariants

Pour obtenir un tableau des conditions invariantes pour les termes courants dans la réflexion pour les types génériques, consultez Type.IsGenericType. Pour obtenir des termes supplémentaires relatifs aux méthodes génériques, consultez MethodBase.IsGenericMethod.