Reflexión y tipos genéricos

Desde el punto de vista de la reflexión, la diferencia entre un tipo genérico y un tipo ordinario es que un tipo genérico se ha asociado a él un conjunto de parámetros de tipo (si es una definición de tipo genérico) o argumentos de tipo (si es un tipo construido). Un método genérico difiere de un método normal de la misma manera.

Hay dos claves para entender cómo controla la reflexión los tipos y métodos genéricos:

  • Los parámetros de tipo de definiciones de tipo genérico y definiciones de método genérico están representados por instancias de la clase Type .

    Nota

    Muchas propiedades y métodos de Type tienen un comportamiento diferente cuando un objeto Type representa un parámetro de tipo genérico. Estas diferencias se documentan en los artículos de propiedades y métodos. Por ejemplo, vea IsAutoClass y DeclaringType. Además, algunos miembros son válidos solamente cuando un objeto Type representa un parámetro de tipo genérico. Por ejemplo, vea GetGenericTypeDefinition.

  • Si una instancia de Type representa un tipo genérico, incluye una matriz de tipos que representan los parámetros de tipo (para definiciones de tipo genérico) o argumentos de tipo (para tipos construidos). Lo mismo puede decirse de una instancia de la clase MethodInfo que representa un método genérico.

La reflexión proporciona métodos de Type y MethodInfo que le permiten acceder a la matriz de parámetros de tipo y determinar si una instancia de Type representa un parámetro de tipo o un tipo real.

Para obtener código de ejemplo en donde se muestran los métodos tratados aquí, vea Procedimiento: Examinar y crear instancias de tipos genéricos mediante la reflexión.

El siguiente análisis supone que está familiarizado con la terminología de los genéricos, como la diferencia entre los parámetros y argumentos de tipo y los tipos construidos abiertos o cerrados. Para más información, vea Genéricos.

¿Es un método o tipo genérico?

Cuando se usa la reflexión para examinar un tipo desconocido, representado por una instancia de Type, use la propiedad IsGenericType para determinar si el tipo desconocido es genérico. Devuelve true si el tipo es genérico. De forma similar, cuando examine un método desconocido, representado por una instancia de la clase MethodInfo , use la propiedad IsGenericMethod para determinar si el método es genérico.

¿Se trata de un tipo genérico o una definición de método?

Use la propiedad IsGenericTypeDefinition para determinar si un objeto Type representa una definición de tipo genérico y use el método IsGenericMethodDefinition para determinar si MethodInfo representa una definición de método genérico.

Las definiciones de tipos y métodos genéricos son las plantillas a partir de las que se crean los tipos instanciables. Los tipos genéricos de las bibliotecas de .NET, como Dictionary<TKey,TValue>, son definiciones de tipos genéricos.

¿El tipo o el método están abiertos o cerrados?

Un tipo o método genérico es cerrado si los tipos instanciables se sustituyeron por todos sus parámetros de tipo, incluidos todos los parámetros de tipo de todos los tipos envolventes. Solo puede crear una instancia de un tipo genérico si está cerrada. La propiedad Type.ContainsGenericParameters devuelve true si un tipo es abierto. En el caso de los métodos, el método MethodBase.ContainsGenericParameters realiza la misma función.

Generación de tipos genéricos cerrados

Cuando tenga una definición de tipo o método genérico, use el método MakeGenericType para crear un tipo genérico cerrado o el método MakeGenericMethod para crear MethodInfo para un método genérico cerrado.

Obtención de la definición de método o tipo genérico

Si tiene un método o tipo genérico abierto que no es un tipo genérico o una definición de método, no puede crear instancias de él y no puede proporcionar los parámetros de tipo que faltan. Debe tener una definición de tipo o método genérico. Use el método GetGenericTypeDefinition para obtener la definición de tipo genérico o el método GetGenericMethodDefinition para obtener la definición de método genérico.

Por ejemplo, si tiene un objeto Type que representa Dictionary<int, string> y desea crear el tipo Dictionary<string, MyClass>, puede usar el método GetGenericTypeDefinition para obtener un Type que represente Dictionary<TKey, TValue> y, a continuación, use el método MakeGenericType para generar un Type que represente Dictionary<int, MyClass>.

Para obtener un ejemplo de un tipo genérico abierto que no es un tipo genérico, vea parámetro de tipo o argumento de tipo.

Examen de argumentos de tipo y parámetros de tipo

Use el método Type.GetGenericArguments para obtener una matriz de objetos Type que representan los parámetros de tipo o argumentos de tipo de un tipo genérico, y use el método MethodInfo.GetGenericArguments para hacer lo mismo para un método genérico.

Una vez que sepa que un objeto Type representa un parámetro de tipo, se plantean muchas otras preguntas que puede responder la reflexión. Puede determinar el origen del parámetro de tipo, su posición y sus restricciones.

Parámetro de tipo o argumento de tipo

Para determinar si un elemento determinado de la matriz es un parámetro de tipo o un argumento de tipo, use la propiedad IsGenericParameter . La propiedad IsGenericParameter es true si el elemento es un parámetro de tipo.

Un tipo genérico puede ser abierto sin necesidad de ser una definición de tipo genérico, en cuyo caso tiene una mezcla de argumentos de tipo y parámetros de tipo. Por ejemplo, en el código siguiente, la clase D deriva de un tipo creado sustituyendo el primer parámetro de tipo de D por el segundo parámetro de tipo de 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 obtiene un objeto Type que representa D<V, W> y usa la propiedad BaseType para obtener su tipo base, el type B<int, V> resultante está abierto, pero no es una definición de tipo genérico.

Origen de un parámetro genérico

Un parámetro de tipo genérico puede proceder del tipo que se está examinando, de un tipo envolvente o de un método genérico. Puede determinar el origen del parámetro de tipo genérico tal como se indica a continuación:

  • En primer lugar, use la propiedad DeclaringMethod para determinar si el parámetro de tipo procede de un método genérico. Si el valor de la propiedad no es una referencia nula, el origen es un método genérico.
  • Si el origen no es un método genérico, use la propiedad DeclaringType para determinar a qué tipo genérico pertenece el parámetro de tipo genérico.

Si el parámetro de tipo pertenece a un método genérico, la propiedad DeclaringType devuelve el tipo que declaró el método genérico, que es irrelevante.

Posición de un parámetro genérico

En raras situaciones, es necesario determinar la posición de un parámetro de tipo en la lista de parámetros de tipo de su clase declarante. Por ejemplo, suponga que tiene un objeto Type que representa el tipo B<int, V> del ejemplo anterior. El método GetGenericArguments proporciona una lista de argumentos de tipo, así que cuando examine V puede usar las propiedades DeclaringMethod y DeclaringType para descubrir su procedencia. A continuación, puede usar la propiedad GenericParameterPosition para determinar su posición en la lista de parámetros de tipo donde se definió. En este ejemplo, V está en la posición 0 (cero) en la lista de parámetros de tipo donde se definió.

Restricciones de interfaz y tipo base

Use el método GetGenericParameterConstraints para obtener la restricción de tipo base y las restricciones de interfaz de un parámetro de tipo. El orden de los elementos de la matriz no es significativo. Un elemento representa una restricción de interfaz si es un tipo de interfaz.

Atributos de parámetro genéricos

La propiedad GenericParameterAttributes obtiene un valor GenericParameterAttributes que indica la varianza (covarianza o contravarianza) y las restricciones especiales de un parámetro de tipo.

Covarianza y contravarianza

Para determinar si un parámetro de tipo es covariante o contravariante, aplique la máscara GenericParameterAttributes.VarianceMask al valor GenericParameterAttributes devuelto por la propiedad GenericParameterAttributes . Si el resultado es GenericParameterAttributes.None, el parámetro de tipo es invariable. Para obtener más información, vea Covarianza y contravarianza.

Restricciones especiales

Para determinar las restricciones especiales de un parámetro de tipo, aplique la máscara GenericParameterAttributes.SpecialConstraintMask al valor GenericParameterAttributes devuelto por la propiedad GenericParameterAttributes . Si el resultado es GenericParameterAttributes.None, no hay ninguna restricción especial. Un parámetro de tipo puede restringirse para que sea un tipo de referencia, para que sea un tipo de valor que no acepta valores NULL y para que tenga un constructor sin parámetros.

Invariables

Para consultar una tabla con las condiciones invariables de los términos comunes usados en la reflexión para tipos genéricos, vea Type.IsGenericType. Para obtener términos adicionales relativos a los métodos genéricos, vea MethodBase.IsGenericMethod.