Отражение и универсальные типы

С точки зрения отражения разница между универсальным типом и обычным типом заключается в том, что универсальный тип связан с ним набором параметров типа (если это определение универсального типа) или аргументы типа (если это созданный тип). Универсальный метод отличается от обычного тем же.

Существует два важных аспекта, необходимых, чтобы понять, как отражение обрабатывает универсальные типы и методы.

  • Параметры типа определений универсальных типов и определений универсальных методов представлены экземплярами класса Type .

    Примечание.

    Многие свойства и методы класса Type функционируют иначе, если объект Type представляет параметр универсального типа. Эти различия описаны в статьях свойств и методов. Например, см. класс IsAutoClass и тип DeclaringType. Кроме того, некоторые элементы действительны, только если объект Type представляет параметр универсального типа. Например, ознакомьтесь со статьей GetGenericTypeDefinition.

  • Если экземпляр типа Type представляет универсальный тип, он содержит массив типов, которые представляют параметры типа (для определений универсального типа) или аргументы типа (для сконструированных типов). То же самое справедливо для экземпляра класса MethodInfo , который представляет универсальный метод.

Отражение предоставляет методы Type и MethodInfo, которые позволяют получить доступ к массиву параметров типа и определить, представляет ли экземпляр Type параметр типа или фактический тип.

Пример кода, демонстрирующий описанные здесь методы, см. в статье Практическое руководство. Изучение универсальных типов и создание их экземпляров с помощью отражения.

В следующем обсуждении предполагается, что вы знакомы с терминологией универсальности, то есть понимаете различия между параметрами типа и аргументами, а также открытыми и закрытыми сконструированными типами. Дополнительные сведения см. в статье Универсальные шаблоны.

Это универсальный тип или метод?

При использовании отражения для изучения неизвестного типа, представленного экземпляром Type, используйте свойство IsGenericType , чтобы определить, является ли неизвестный тип универсальным. Возвращается значение true , если тип является универсальным. Аналогично при рассмотрении неизвестного метода, представленного экземпляром класса MethodInfo , воспользуйтесь свойством IsGenericMethod , чтобы установить, является ли метод универсальным.

Это определение универсального типа или метода?

Используйте свойство IsGenericTypeDefinition , чтобы установить, представляет ли объект Type определение универсального типа. Используйте метод IsGenericMethodDefinition , чтобы определить, представляет ли MethodInfo определение универсального метода.

Определения универсальных типов и методов — это шаблоны, из которых создаются типы с возможностью создания экземпляров. Универсальные типы в библиотеках .NET, например Dictionary<TKey,TValue>, являются определениями универсальных типов.

Открыт или закрыт ли тип или метод?

Универсальный тип или метод является закрытым, если все его параметры типов были заменены типами с возможностью создания экземпляров, включая все параметры всех содержащихся типов. Экземпляр универсального типа можно создать только в том случае, если он закрыт. Свойство Type.ContainsGenericParameters возвращает значение true , если тип открыт. Для методов метод MethodBase.ContainsGenericParameters выполняет ту же функцию.

Создание закрытых универсальных типов

Получив определение универсального типа или метода, воспользуйтесь методом MakeGenericType для создания закрытого универсального типа или методом MakeGenericMethod для создания MethodInfo для закрытого универсального метода.

Получение определения универсального типа или метода

Если у вас есть открытый универсальный тип или метод, который не является универсальным типом или определением метода, невозможно создать экземпляры этого типа и указать отсутствующие параметры типа. Необходимо иметь определение универсального типа или метода. Используйте метод GetGenericTypeDefinition для получения определения универсального типа или метод GetGenericMethodDefinition для получения определения универсального метода.

Например, если у вас есть Type объект, представляющий Dictionary<int, string> и вы хотите создать тип Dictionary<string, MyClass>, можно использовать GetGenericTypeDefinition метод для получения Type представления Dictionary<TKey, TValue> , а затем использовать MakeGenericType метод для создания Type представления Dictionary<int, MyClass>.

Пример открытого универсального типа, который не является универсальным типом, см . в разделе "Параметр типа" или аргумент типа.

Проверка аргументов типа и параметров типа

Используйте метод Type.GetGenericArguments , чтобы получить массив объектов Type , которые представляют параметры типа или аргументы типа для универсального типа; используйте метод MethodInfo.GetGenericArguments , чтобы выполнить то же самое для универсального метода.

Если известно, что объект Type представляет параметр типа, отражение может ответить на множество дополнительных вопросов. Можно определить источник параметра типа, его местоположение и ограничения.

Аргумент типа или аргумент типа

Чтобы определить, является ли конкретный элемент массива параметром типа или аргументом типа, используйте свойство IsGenericParameter . Свойство IsGenericParameter имеет значение true , если элемент является параметром типа.

Универсальный тип может быть открытым, не являясь определением универсального типа. В этом случае он содержит как аргументы типа, так и параметры типа. Например, в следующем коде класс D является производным от типа, созданного путем замены первого параметра типа класса D вторым параметром типа 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> {};

Если вы получите Type объект, представляющий D<V, W> и используйте BaseType свойство для получения базового типа, результат type B<int, V> открыт, но это не определение универсального типа.

Источник универсального параметра

Параметр универсального типа может происходить от рассматриваемого типа, вмещающего типа или универсального метода. Определить источник параметра универсального типа можно следующим образом.

  • Во-первых, используйте свойство DeclaringMethod , чтобы определить, является ли параметр типа производным от универсального метода. Если значение свойства не является пустой ссылкой, источник является универсальным методом.
  • Если источник не является универсальным методом, используйте свойство DeclaringType , чтобы определить универсальный тип, к которому относится параметр универсального типа.

Если параметр типа относится к универсальному методу, свойство DeclaringType возвращает тип, объявивший универсальный метод, что не имеет смысла.

Позиция универсального параметра

В редких ситуациях необходимо определить положение параметра типа в списке параметров типа его декларативного класса. Допустим, имеется объект Type , представляющий тип B<int, V> из предыдущего примера. Метод GetGenericArguments предоставляет список аргументов типа, и при рассмотрении V можно воспользоваться свойствами DeclaringMethod и DeclaringType , чтобы установить его источник. Затем можно использовать свойство GenericParameterPosition , чтобы определить его положение в списке параметров типа, в котором он был определен. В этом примере V находится в положении 0 (ноль) в списке параметров типа, где он был определен.

Ограничения базового типа и интерфейса

Используйте метод GetGenericParameterConstraints для получения ограничений базового типа и ограничений интерфейса параметра типа. Порядок элементов массива не имеет значения. Элемент представляет ограничение интерфейса, если это тип интерфейса.

Атрибуты универсальных параметров

Свойство GenericParameterAttributes получает значение GenericParameterAttributes , которое указывает на дисперсию (ковариация и контрвариация) и особые ограничения параметра типа.

Ковариантность и контрвариантность

Чтобы определить, является ли параметр типа ковариантным или контравариантным, примените маску GenericParameterAttributes.VarianceMask к значению GenericParameterAttributes , возвращаемому свойством GenericParameterAttributes . Если результатом является GenericParameterAttributes.None, параметр типа является инвариантным. Дополнительные сведения см. в разделе Ковариация и контравариантность.

Специальные ограничения

Чтобы определить особые ограничения параметра типа, примените маску GenericParameterAttributes.SpecialConstraintMask к значению GenericParameterAttributes , возвращаемому свойством GenericParameterAttributes . Если результатом является GenericParameterAttributes.None, специальные ограничения отсутствуют. Параметр типа может быть ограничен ссылочным типом, являться типом значения, не допускающим значения NULL, и иметь конструктор без параметров.

Инварианты

Таблицу неизменяемых условий для общих терминов отражения универсальных типов см. в разделе Type.IsGenericType. Дополнительные термины, связанные с универсальными методами, см. в разделе MethodBase.IsGenericMethod.