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


Метод 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 метод.

Замечание

Конструированный тип, такой как Base<int, V>, полезен при создании кода, но вы не можете вызвать метод MakeGenericType для этого типа, потому что Base<int, V> это не определение универсального типа. Чтобы создать закрытый сконструированный тип, который может быть создан, сначала вызовите метод GetGenericTypeDefinition для получения объекта Type, представляющего определение обобщённого типа, а затем вызовите MakeGenericType с требуемыми аргументами типа.

Объект Type, возвращаемый MakeGenericType, идентичен тому объекту, который получается путем вызова метода Type результирующего созданного типа или метода GetType любого созданного типа, который был создан из того же определения универсального типа с использованием тех же типов аргументов.

Замечание

Массив универсальных типов не является универсальным типом. Невозможно вызвать MakeGenericType для типа массива, такого как C<T>[] (Dim ac() As C(Of T) в Visual Basic). Чтобы построить закрытый универсальный тип из C<T>[], выполните вызов GetElementType, чтобы получить определение универсального типа C<T>; выполните вызов MakeGenericType на определение универсального типа, чтобы создать сконструированный тип; и, наконец, вызовите метод 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 имеет три параметра типа: T, U и 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 {
        }
    }
}

В этом примере невозможно определить поле типа T или U в классе Innermost, так как эти параметры типа не находятся в области видимости. Следующий код сборщика определяет вложенные классы, которые ведут себя так, как они будут определяться в 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) для изучения вложенных классов, определенных на высокоуровневых языках, и наблюдать за этой схемой именования.