Поделиться через


Рефлексия и универсальные типы

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

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

  • Параметры типа определений универсальных типов и определений универсальных методов представлены экземплярами 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

Если вы получите 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.