Reflexão e tipos genéricos

Do ponto de vista da reflexão, a diferença entre um tipo genérico e um tipo comum é que um tipo genérico tem associado a ele um conjunto de parâmetros de tipo (se for uma definição de tipo genérico) ou argumentos de tipo (se for um tipo construído). Um método genérico é diferente de um método comum da mesma maneira.

Há duas chaves para entender como a reflexão trata tipos e métodos genéricos:

  • Os parâmetros de tipo de definições de tipo genérico e definições de método genérico são representadas por instâncias da classe Type.

    Observação

    Muitas propriedades e métodos de Type têm um comportamento diferente quando um objeto Type representa um parâmetro de tipo genérico. Essas diferenças são documentadas nos artigos de propriedade e método. Por exemplo, veja IsAutoClass e DeclaringType. Além disso, alguns membros são válidos somente quando um objeto Type representa um parâmetro de tipo genérico. Por exemplo, veja GetGenericTypeDefinition.

  • Se uma instância do Type representa um tipo genérico, ela inclui uma matriz de tipos que representam os parâmetros de tipo (para definições de tipo genérico) ou os argumentos de tipo (para tipos construídos). O mesmo é verdadeiro para uma instância da classe MethodInfo que representa um método genérico.

A reflexão fornece métodos de Type e MethodInfo que permitem a você acessar a matriz de parâmetros de tipo e determinar se uma instância de Type representa um parâmetro de tipo ou um tipo real.

Para obter o código de exemplo que demonstra os métodos discutidos aqui, consulte Como examinar tipos genéricos e criar instâncias deles com a reflexão.

A discussão a seguir pressupõe familiaridade com a terminologia de genéricos, como a diferença entre os parâmetros e argumentos de tipo e tipos construídos abertos ou fechados. Para obter mais informações, consulte Genéricos.

Este é um tipo ou um método genérico?

Quando usar a reflexão para examinar um tipo desconhecido, representado por uma instância de Type, use a propriedade IsGenericType para determinar se o tipo desconhecido é genérico. Ele retornará true se o tipo for genérico. Da mesma forma, quando você examinar um método desconhecido, representado por uma instância da classe MethodInfo, use a propriedade IsGenericMethod para determinar se o método é genérico.

Esta é uma definição de tipo ou método genérico?

Use a propriedade IsGenericTypeDefinition para determinar se um objeto Type representa uma definição de tipo genérico e use o método IsGenericMethodDefinition para determinar se um MethodInfo representa uma definição de método genérico.

As definições de tipo e método genéricos são os modelos dos quais os tipos instanciáveis são criados. Os tipos genéricos nas bibliotecas do .NET, como Dictionary<TKey,TValue>, são definições de tipo genérico.

O tipo ou o método é aberto ou fechado?

Um tipo ou método genérico será fechado se os tipos instanciáveis tiverem sido substituídos para todos os seus parâmetros de tipo, incluindo todos os parâmetros de tipo de todos os tipos delimitadores. Você poderá criar uma instância de um tipo genérico somente se ele for fechado. A propriedade Type.ContainsGenericParameters retornará true se um tipo for aberto. Para métodos, o método MethodBase.ContainsGenericParameters executa a mesma função.

Gerar tipos genéricos fechados

Quando você tiver uma definição de método ou tipo genérico, use o método MakeGenericType para criar um tipo genérico fechado ou o método MakeGenericMethod para criar um MethodInfo para um método genérico fechado.

Obter a definição do tipo ou método genérico

Se você tiver um método ou tipo genérico aberto que não seja uma definição de método ou tipo genérico, não poderá criar instâncias dele e não poderá fornecer os parâmetros de tipo que estão faltando. Você deve ter uma definição de tipo ou método genérico. Use o método GetGenericTypeDefinition para obter a definição de tipo genérico ou o método GetGenericMethodDefinition para obter a definição de método genérico.

Por exemplo, se você tiver um objeto Type representando Dictionary<int, string> e desejar criar o tipo Dictionary<string, MyClass>, poderá usar o método GetGenericTypeDefinition para obter um Type representando Dictionary<TKey, TValue> e, em seguida, usar o método MakeGenericType para produzir um Type representando Dictionary<int, MyClass>.

Para obter um exemplo de um tipo genérico aberto que não é um tipo genérico, consulte Parâmetro de tipo ou argumento de tipo.

Examinar os parâmetros de tipo e os argumentos de tipo

Use o método Type.GetGenericArguments para obter uma matriz de objetos Type que representam os parâmetros de tipo ou os argumentos de tipo de um tipo genérico e use o método MethodInfo.GetGenericArguments para fazer o mesmo para um método genérico.

Uma vez que você souber que um objeto Type representa um parâmetro de tipo, há muitas perguntas adicionais que a reflexão pode responder. Você pode determinar a origem do parâmetro de tipo, sua posição e suas restrições.

Parâmetro de tipo ou argumento de tipo

Para determinar se um elemento específico da matriz é um parâmetro de tipo ou um argumento de tipo, use a propriedade IsGenericParameter. A propriedade IsGenericParameter será true se o elemento for um parâmetro de tipo.

Um tipo genérico pode ser aberto sem ser uma definição de tipo genérico, nesse caso, ele tem uma mistura de argumentos de tipo e parâmetros de tipo. Por exemplo, no código a seguir, a classe D deriva de um tipo criado substituindo o primeiro parâmetro de tipo de D pelo 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> {};

Se você obtiver um objeto Type representando D<V, W> e usar a propriedade BaseType para obter seu tipo base, o type B<int, V> resultante será aberto, mas não será uma definição de tipo genérico.

Origem de um parâmetro genérico

Um parâmetro de tipo genérico pode vir do tipo sendo examinado, de um tipo delimitador ou de um método genérico. Você pode determinar a origem do parâmetro de tipo genérico da seguinte maneira:

  • Primeiro, use a propriedade DeclaringMethod para determinar se o parâmetro de tipo é proveniente de um método genérico. Se o valor da propriedade não for uma referência nula, a origem será um método genérico.
  • Se a origem não for um método genérico, use a propriedade DeclaringType para determinar o tipo genérico ao qual o parâmetro de tipo genérico pertence.

Se o parâmetro de tipo pertencer a um método genérico, a propriedade DeclaringType retornará o tipo que declarou o método genérico, o que é irrelevante.

Posição de um parâmetro genérico

Em raras situações, é necessário determinar a posição de um parâmetro de tipo na lista de parâmetros de tipo de sua classe declarante. Por exemplo, suponha que você tenha um objeto Type representando o tipo B<int, V> do exemplo anterior. O método GetGenericArguments fornece uma lista de argumentos de tipo e ao examinar V você pode usar as propriedades DeclaringMethod e DeclaringType para descobrir de onde ele vem. Você pode usar a propriedade GenericParameterPosition para determinar sua posição na lista de parâmetros de tipo em que foi definido. Neste exemplo, V está na posição 0 (zero) na lista de parâmetros de tipo em que foi definido.

Tipo base e restrições de interface

Use o método GetGenericParameterConstraints para obter a restrição do tipo base e as restrições de interface de um parâmetro de tipo. A ordem dos elementos da matriz não é significativa. Um elemento representará uma restrição de interface se ele for um tipo de interface.

Atributos de parâmetro genérico

A propriedade GenericParameterAttributes obtém um valor GenericParameterAttributes que indica a variância (covariância ou contravariância) e as restrições especiais de um parâmetro de tipo.

Covariância e contravariância

Para determinar se um parâmetro de tipo é covariante ou contravariante, aplique a máscara GenericParameterAttributes.VarianceMask ao valor GenericParameterAttributes retornado pela propriedade GenericParameterAttributes. Se o resultado for GenericParameterAttributes.None, o parâmetro de tipo será invariável. Para obter mais informações, consulte Covariância e contravariância.

Restrições especiais

Para determinar as restrições especiais de um parâmetro de tipo, aplique a máscara GenericParameterAttributes.SpecialConstraintMask ao valor GenericParameterAttributes retornado pela propriedade GenericParameterAttributes. Se o resultado for GenericParameterAttributes.None, não haverá nenhuma restrição especial. Um parâmetro de tipo pode ser restringido para ser um tipo de referência, para ser um tipo de valor não nulo e para ter um construtor sem parâmetros.

Invariáveis

Para obter uma tabela de condições invariáveis para termos comuns na reflexão de tipos genéricos, consulte Type.IsGenericType. Para encontrar termos adicionais relacionados aos métodos genéricos, consulte MethodBase.IsGenericMethod.