Метод System.Type.MakeGenericType
В этой статье приводятся дополнительные замечания к справочной документации по этому API.
Этот MakeGenericType метод позволяет писать код, который назначает определенные типы параметрам типа определения универсального типа, создавая таким образом Type объект, представляющий конкретный созданный тип. Этот Type объект можно использовать для создания экземпляров времени выполнения созданного типа.
Типы, созданные с MakeGenericType помощью открытых, то есть некоторые из их аргументов типа могут быть параметрами типа для заключения универсальных методов или типов. При отправке динамических сборок можно использовать такие открытые созданные типы. Например, рассмотрим классы Base
и Derived
в следующем коде.
public class Base<T, U> { }
public class Derived<V> : Base<int, V> { }
type Base<'T, 'U>() = class end
type Derived<'V>() = inherit Base<int, 'V>()
Public Class Base(Of T, U)
End Class
Public Class Derived(Of V)
Inherits Base(Of Integer, V)
End Class
Чтобы создать Derived
динамическую сборку, необходимо создать его базовый тип. Для этого вызовите MakeGenericType метод объекта Type , представляющего класс Base
, используя аргументы Int32 универсального типа и параметр V
типа из Derived
. Так как типы и параметры универсального типа представлены Type объектами, массив, содержащий оба типа, можно передать в MakeGenericType метод.
Примечание.
Созданный тип, например, полезен при создании кода, но нельзя вызвать MakeGenericType метод для этого типа, так как Base<int, V>
это не определение универсального типа. Чтобы создать закрытый созданный тип, который можно создать, сначала вызовите GetGenericTypeDefinition метод для получения Type объекта, представляющего определение универсального типа, а затем вызовите MakeGenericType аргументы требуемого типа.
Возвращаемый TypeMakeGenericType объектом является тот же, что Type и полученный путем вызова GetType метода результирующего созданного типа или GetType метода любого созданного типа, созданного из того же определения универсального типа с использованием аргументов одного типа.
Примечание.
Массив универсальных типов не является универсальным типом. Невозможно вызвать MakeGenericType тип массива, например C<T>[]
(Dim ac() As C(Of T)
в Visual Basic). Чтобы создать закрытый универсальный тип, C<T>[]
вызов для получения определения C<T>
универсального типа; вызов MakeGenericTypeGetElementType определения универсального типа для создания созданного типа; и, наконец, вызов MakeArrayType метода в созданном типе для создания типа массива. То же самое относится к типам указателей и ref
типам (ByRef
в Visual Basic).
Список неизменяемых условий для терминов, используемых в отражении универсальных типов, см. в примечаниях к описанию свойства IsGenericType.
Вложенные типы
Если универсальный тип определен с помощью C#, C++или Visual Basic, вложенные типы являются универсальными. Это верно, даже если вложенные типы не имеют собственных параметров типа, так как все три языка включают параметры типа в списки параметров типа вложенных типов. Рассмотрим следующие классы:
public class Outermost<T>
{
public class Inner<U>
{
public class Innermost1<V> {}
public class Innermost2 {}
}
}
Public Class Outermost(Of T)
Public Class Inner(Of U)
Public Class Innermost1(Of V)
End Class
Public Class Innermost2
End Class
End Class
End Class
Список параметров типа вложенного класса Inner
имеет два параметра типа, T
и U
первый из которых является параметром типа его заключенного класса. Аналогичным образом, список параметров типа вложенного класса Innermost1
имеет три параметра типа, U
T
и , а V
T
U
также, поступающие из его вложенных классов. Вложенный класс Innermost2
имеет два параметра типа и T
U
, которые приходят из его вложенных классов.
Если список параметров включаемого типа имеет несколько параметров типа, все параметры типа в порядке включаются в список параметров типа вложенного типа.
Чтобы создать универсальный тип из определения универсального типа для вложенного типа, вызовите MakeGenericType метод с массивом, сформированным путем объединения массивов аргументов типа всех вложенных типов, начиная с самого внешнего универсального типа и заканчивая массивом аргументов типа вложенного типа, если он имеет собственные параметры типа. Чтобы создать экземпляр Innermost1
, вызовите MakeGenericType метод с массивом, содержащим три типа, для назначения T, U и V. Чтобы создать экземпляр Innermost2
, вызовите MakeGenericType метод с массивом, содержащим два типа, для назначения T и U.
Языки распространяют параметры типа включаемых типов таким образом, чтобы можно было использовать параметры типа включающего типа для определения полей вложенных типов. В противном случае параметры типа не будут находиться в область в телах вложенных типов. Можно определить вложенные типы без распространения параметров типа вложенных типов путем создания кода в динамических сборках или с помощью Ilasm.exe (сборщик IL). Рассмотрим следующий код для сборщика CIL:
.class public Outer<T> {
.class nested public Inner<U> {
.class nested public Innermost {
}
}
}
В этом примере невозможно определить поле типа или U
классаInnermost
, так как эти параметры типа T
не находятся в область. Следующий код сборщика определяет вложенные классы, которые ведут себя так, как они будут определяться в C++, Visual Basic и C#:
.class public Outer<T> {
.class nested public Inner<T, U> {
.class nested public Innermost<T, U, V> {
}
}
}
Вы можете использовать Ildasm.exe (IL Disassembler) для изучения вложенных классов, определенных на высокоуровневых языках, и наблюдать за этой схемой именования.