Como: Definir um tipo genérico com reflexão emitir

Este tópico mostra como criar um tipo genérico simples com dois tipos de parâmetros, como aplicar restrições de classe, restrições de interface e restrições especiais aos parâmetros de tipo e como criar membros que usam os parâmetros de tipo da classe como tipos de parâmetro e tipos de retorno.

Observação importanteImportante

Um método não é genérico porque ele pertence a um tipo genérico e usa os parâmetros de tipo desse tipo.Um método é genérico somente se ele tiver sua própria lista de parâmetro de tipo.A maioria dos métodos em tipos genéricos não são genéricas, como no exemplo.Para obter um exemplo emitindo um método genérico, consulte Como: Definir um método genérico com reflexão emitir.

Para definir um tipo genérico

  1. Definir um assembly dinâmico denominado GenericEmitExample1. Neste exemplo, o assembly é executado e salvas em disco, isso AssemblyBuilderAccess.RunAndSave é especificado.

    Dim myDomain As AppDomain = AppDomain.CurrentDomain
    Dim myAsmName As New AssemblyName("GenericEmitExample1")
    Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
        myAsmName, _
        AssemblyBuilderAccess.RunAndSave)
    
    AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
    AssemblyBuilder myAssembly = 
        myDomain.DefineDynamicAssembly(myAsmName, 
            AssemblyBuilderAccess.RunAndSave);
    
    AppDomain^ myDomain = AppDomain::CurrentDomain;
    AssemblyName^ myAsmName = gcnew AssemblyName( L"GenericEmitExample1" );
    AssemblyBuilder^ myAssembly = myDomain->DefineDynamicAssembly( 
        myAsmName, AssemblyBuilderAccess::RunAndSave );
    
  2. Defina um módulo dinâmico. Um assembly é composto de módulos do executáveis. Para um assembly de módulo único, o nome do módulo é igual ao nome de assembly e o nome do arquivo é o nome do módulo mais uma extensão.

    Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
        myAsmName.Name, _
        myAsmName.Name & ".dll")
    
    ModuleBuilder myModule = 
        myAssembly.DefineDynamicModule(myAsmName.Name, 
           myAsmName.Name + ".dll");
    
    ModuleBuilder^ myModule = myAssembly->DefineDynamicModule( 
        myAsmName->Name, String::Concat( myAsmName->Name, L".dll" ) );
    
  3. Defina uma classe. Neste exemplo, a classe é nomeada Sample.

    Dim myType As TypeBuilder = myModule.DefineType( _
        "Sample", _
        TypeAttributes.Public)
    
    TypeBuilder myType = 
        myModule.DefineType("Sample", TypeAttributes.Public);
    
    TypeBuilder^ myType = myModule->DefineType( L"Sample", 
        TypeAttributes::Public );
    
  4. Defina os parâmetros de tipo genérico de Sample , transmitindo uma matriz de seqüências de caracteres que contém os nomes dos parâmetros para o TypeBuilder.DefineGenericParameters método. Isso torna a classe de um tipo genérico. O valor de retorno é uma matriz de GenericTypeParameterBuilder objetos que representam os parâmetros de tipo, que podem ser usados em seu código emitido.

    No código a seguir, Sample se torna um tipo genérico com parâmetros de tipo TFirst e TSecond. Para tornar o código mais fácil de ler cada GenericTypeParameterBuilder é colocado em uma variável com o mesmo nome de parâmetro de tipo.

    Dim typeParamNames() As String = {"TFirst", "TSecond"}
    Dim typeParams() As GenericTypeParameterBuilder = _
        myType.DefineGenericParameters(typeParamNames)
    
    Dim TFirst As GenericTypeParameterBuilder = typeParams(0)
    Dim TSecond As GenericTypeParameterBuilder = typeParams(1)
    
    string[] typeParamNames = {"TFirst", "TSecond"};
    GenericTypeParameterBuilder[] typeParams = 
        myType.DefineGenericParameters(typeParamNames);
    
    GenericTypeParameterBuilder TFirst = typeParams[0];
    GenericTypeParameterBuilder TSecond = typeParams[1];
    
    array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
    array<GenericTypeParameterBuilder^>^typeParams = 
        myType->DefineGenericParameters( typeParamNames );
    
    GenericTypeParameterBuilder^ TFirst = typeParams[0];
    GenericTypeParameterBuilder^ TSecond = typeParams[1];
    
  5. Adicione restrições especiais aos parâmetros de tipo. Neste exemplo, o parâmetro de tipo TFirst é restrito para tipos que possuem construtores sem parâmetros e tipos de referência.

    TFirst.SetGenericParameterAttributes( _
        GenericParameterAttributes.DefaultConstructorConstraint _
        Or GenericParameterAttributes.ReferenceTypeConstraint)
    
    TFirst.SetGenericParameterAttributes(
        GenericParameterAttributes.DefaultConstructorConstraint |
        GenericParameterAttributes.ReferenceTypeConstraint);
    
    TFirst->SetGenericParameterAttributes( 
        GenericParameterAttributes::DefaultConstructorConstraint | 
        GenericParameterAttributes::ReferenceTypeConstraint 
    );
    
  6. Opcionalmente, adicione as restrições de classe e a interface para os parâmetros de tipo. Neste exemplo, o parâmetro de tipo TFirst é restrita a tipos que derivam da classe base representada pela Type objeto contido na variável baseType, e que implementam as interfaces cujos tipos estão contidos na caixa variáveis de interfaceA e interfaceB. Consulte o exemplo de código para a declaração e a atribuição dessas variáveis.

    TSecond.SetBaseTypeConstraint(baseType)
    Dim interfaceTypes() As Type = {interfaceA, interfaceB}
    TSecond.SetInterfaceConstraints(interfaceTypes)
    
    TSecond.SetBaseTypeConstraint(baseType);
    Type[] interfaceTypes = {interfaceA, interfaceB};
    TSecond.SetInterfaceConstraints(interfaceTypes);
    
    array<Type^>^interfaceTypes = { interfaceA, interfaceB };
    TSecond->SetInterfaceConstraints( interfaceTypes );
    TSecond->SetBaseTypeConstraint( baseType );
    
  7. Defina um campo. Neste exemplo, o tipo do campo especificado pelo parâmetro do tipo TFirst. GenericTypeParameterBuilderderiva de Type, portanto, você pode usar parâmetros de tipo genérico em qualquer lugar que um tipo pode ser usado.

    Dim exField As FieldBuilder = _
        myType.DefineField("ExampleField", TFirst, _
            FieldAttributes.Private)
    
    FieldBuilder exField = 
        myType.DefineField("ExampleField", TFirst, 
            FieldAttributes.Private);
    
    FieldBuilder^ exField = 
        myType->DefineField("ExampleField", TFirst, 
            FieldAttributes::Private);
    
  8. Defina um método que usa os parâmetros de tipo do tipo genérico. Observe que esses métodos não são genéricos, a menos que tenham suas próprias listas de parâmetro de tipo. O código a seguir define uma static método (Shared em Visual Basic) que leva a uma matriz de TFirst e retorna um List<TFirst> (List(Of TFirst) em Visual Basic) que contém todos os elementos da matriz. Para definir esse método, é necessário criar o tipo List<TFirst> chamando MakeGenericType na definição de tipo genérico, List<T>. (O T for omitido ao usar o typeof operador (GetType em Visual Basic) para obter a definição de tipo genérico.) O tipo de parâmetro é criado usando o MakeArrayType método.

    Dim listOf As Type = GetType(List(Of ))
    Dim listOfTFirst As Type = listOf.MakeGenericType(TFirst)
    Dim mParamTypes() As Type = { TFirst.MakeArrayType() }
    
    Dim exMethod As MethodBuilder = _
        myType.DefineMethod("ExampleMethod", _
            MethodAttributes.Public Or MethodAttributes.Static, _
            listOfTFirst, _
            mParamTypes)
    
    Type listOf = typeof(List<>);
    Type listOfTFirst = listOf.MakeGenericType(TFirst);
    Type[] mParamTypes = {TFirst.MakeArrayType()};
    
    MethodBuilder exMethod = 
        myType.DefineMethod("ExampleMethod", 
            MethodAttributes.Public | MethodAttributes.Static, 
            listOfTFirst, 
            mParamTypes);
    
    Type^ listOf = List::typeid;
    Type^ listOfTFirst = listOf->MakeGenericType(TFirst);
    array<Type^>^ mParamTypes = { TFirst->MakeArrayType() };
    
    MethodBuilder^ exMethod = 
        myType->DefineMethod("ExampleMethod", 
            MethodAttributes::Public | MethodAttributes::Static, 
            listOfTFirst, 
            mParamTypes);
    
  9. Emita o corpo do método. O corpo do método consiste em três opcodes que carregam a matriz de entrada na pilha, chame o List<TFirst> construtor que leva IEnumerable<TFirst> (que faz todo o trabalho de colocar elementos de entrada na lista) e retornar (deixando a nova List<T> o objeto na pilha). A parte difícil de emitir esse código está recebendo o construtor.

    O GetConstructor método não é suportado em um GenericTypeParameterBuilder, portanto, é não é possível obter o construtor da List<TFirst> diretamente. Primeiro, é necessário obter o construtor da definição de tipo genérico List<T> e, em seguida, chamar um método que converte para o construtor correspondente da List<TFirst>.

    O construtor usado para este exemplo de código utiliza um IEnumerable<T>. Observe, entretanto, isso não é a definição de tipo genérico da IEnumerable<T> interface genérica; em vez disso, o parâmetro de tipo T de List<T> deve ser substituído para o parâmetro de tipo T de IEnumerable<T>. (Isso parece confuso, apenas porque ambos os tipos têm parâmetros de tipo chamados T. Isto é por que este exemplo de código usa os nomes de TFirst e TSecond.) Para obter o tipo de argumento do construtor, comece com a definição de tipo genérico IEnumerable<T> e chame MakeGenericType com o primeiro parâmetro de tipo genérico do List<T>. A lista de argumentos do construtor deve ser passada como uma matriz, com apenas um argumento nesse caso.

    Observação

    A definição de tipo genérico é expresso como IEnumerable<> ao usar o typeof operador em C# ou IEnumerable(Of ) ao usar o GetType operador em Visual Basic.

    Agora é possível obter o construtor da List<T> chamando GetConstructor na definição de tipo genérico. Para converter este construtor para o construtor correspondente da List<TFirst>, passe List<TFirst> e o construtor de List<T> a um estático TypeBuilder.GetConstructor(Type, ConstructorInfo) método.

    Dim ilgen As ILGenerator = exMethod.GetILGenerator()
    
    Dim ienumOf As Type = GetType(IEnumerable(Of ))
    Dim listOfTParams() As Type = listOf.GetGenericArguments()
    Dim TfromListOf As Type = listOfTParams(0)
    Dim ienumOfT As Type = ienumOf.MakeGenericType(TfromListOf)
    Dim ctorArgs() As Type = { ienumOfT }
    
    Dim ctorPrep As ConstructorInfo = _
        listOf.GetConstructor(ctorArgs)
    Dim ctor As ConstructorInfo = _
        TypeBuilder.GetConstructor(listOfTFirst, ctorPrep)
    
    ilgen.Emit(OpCodes.Ldarg_0)
    ilgen.Emit(OpCodes.Newobj, ctor)
    ilgen.Emit(OpCodes.Ret)
    
    ILGenerator ilgen = exMethod.GetILGenerator();
    
    Type ienumOf = typeof(IEnumerable<>);
    Type TfromListOf = listOf.GetGenericArguments()[0];
    Type ienumOfT = ienumOf.MakeGenericType(TfromListOf);
    Type[] ctorArgs = {ienumOfT};
    
    ConstructorInfo ctorPrep = listOf.GetConstructor(ctorArgs);
    ConstructorInfo ctor = 
        TypeBuilder.GetConstructor(listOfTFirst, ctorPrep);
    
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Newobj, ctor);
    ilgen.Emit(OpCodes.Ret);
    
    ILGenerator^ ilgen = exMethod->GetILGenerator();
    
    Type^ ienumOf = IEnumerable::typeid;
    Type^ TfromListOf = listOf->GetGenericArguments()[0];
    Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf);
    array<Type^>^ ctorArgs = {ienumOfT};
    
    ConstructorInfo^ ctorPrep = listOf->GetConstructor(ctorArgs);
    ConstructorInfo^ ctor = 
        TypeBuilder::GetConstructor(listOfTFirst, ctorPrep);
    
    ilgen->Emit(OpCodes::Ldarg_0);
    ilgen->Emit(OpCodes::Newobj, ctor);
    ilgen->Emit(OpCodes::Ret);
    
  10. O tipo de criar e salvar o arquivo.

    Dim finished As Type = myType.CreateType()
    myAssembly.Save(myAsmName.Name & ".dll")
    
    Type finished = myType.CreateType();
    myAssembly.Save(myAsmName.Name+".dll");
    
    Type^ finished = myType->CreateType();
    myAssembly->Save( String::Concat( myAsmName->Name, L".dll" ) );
    
  11. Chame o método. ExampleMethodnão é genérico, mas o tipo que ele pertence é genérico, para obter um MethodInfo que podem ser invocados é necessário criar um tipo de construído a partir da definição de tipo de Sample. O tipo construído usa o Example classe, que satisfaça as restrições em TFirst porque ele é um tipo de referência e tem um construtor sem parâmetros padrão e o ExampleDerived classe que satisfaça as restrições em TSecond. (O código de ExampleDerived podem ser encontradas na seção de código de exemplo.) Esses dois tipos são passados para MakeGenericType para criar o tipo construído. O MethodInfo é obtido, em seguida, usando o GetMethod método.

    Dim typeArgs() As Type = _
        { GetType(Example), GetType(ExampleDerived) }
    Dim constructed As Type = finished.MakeGenericType(typeArgs)
    Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
    
    Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
    Type constructed = finished.MakeGenericType(typeArgs);
    MethodInfo mi = constructed.GetMethod("ExampleMethod");
    
    array<Type^>^ typeArgs = 
        { Example::typeid, ExampleDerived::typeid };
    Type^ constructed = finished->MakeGenericType(typeArgs);
    MethodInfo^ mi = constructed->GetMethod("ExampleMethod");
    
  12. O código a seguir cria uma matriz de Example objetos, coloca essa matriz em uma matriz do tipo Object representando os argumentos do método a ser invocado e os transfere para o Invoke(Object, Object[]) método. O primeiro argumento do Invoke método é uma referência nula, pois o método é static.

    Dim input() As Example = { New Example(), New Example() }
    Dim arguments() As Object = { input }
    
    Dim listX As List(Of Example) = mi.Invoke(Nothing, arguments)
    
    Console.WriteLine(vbLf & _
        "There are {0} elements in the List(Of Example).", _
        listX.Count _ 
    )
    
    Example[] input = {new Example(), new Example()};
    object[] arguments = {input};
    
    List<Example> listX = 
        (List<Example>) mi.Invoke(null, arguments);
    
    Console.WriteLine(
        "\nThere are {0} elements in the List<Example>.", 
        listX.Count);
    
    array<Example^>^ input = { gcnew Example(), gcnew Example() };
    array<Object^>^ arguments = { input };
    
    List<Example^>^ listX = 
        (List<Example^>^) mi->Invoke(nullptr, arguments);
    
    Console::WriteLine(
        "\nThere are {0} elements in the List<Example>.", 
        listX->Count);
    

Exemplo

O exemplo de código a seguir define uma classe chamada Sample, juntamente com uma classe base e duas interfaces. O programa define dois parâmetros de tipo genérico para Sample, transformando-o em um tipo genérico. Os parâmetros de tipo são a única coisa que faz com que um tipo genérico. O programa mostra isso exibindo uma mensagem de teste antes e após a definição de parâmetros de tipo.

O parâmetro de tipo TSecond é usado para demonstrar as restrições de interface e classe, usando a classe base e interfaces e o parâmetro de tipo TFirst é usado para demonstrar as restrições especiais.

O exemplo de código define um campo e um método usando parâmetros de tipo da classe para o tipo de campo e para o parâmetro e tipo do método de retorno.

Após a Sample classe foi criada, o método é invocado.

O programa inclui um método que lista informações sobre um tipo genérico e um método que lista as restrições especiais em um parâmetro de tipo. Esses métodos são usados para exibir informações sobre a conclusão Sample classe.

O programa salva o módulo concluído no disco como GenericEmitExample1.dll, portanto, você poderá abri-lo com o Ildasm. exe (desmontador MSIL) e examine o MSIL para o Sample classe.

Imports System
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Collections.Generic

' Define a trivial base class and two trivial interfaces 
' to use when demonstrating constraints.
'
Public Class ExampleBase
End Class

Public Interface IExampleA
End Interface

Public Interface IExampleB
End Interface

' Define a trivial type that can substitute for type parameter 
' TSecond.
'
Public Class ExampleDerived
    Inherits ExampleBase
    Implements IExampleA, IExampleB
End Class

Public Class Example
    Public Shared Sub Main()
        ' Define a dynamic assembly to contain the sample type. The
        ' assembly will not be run, but only saved to disk, so
        ' AssemblyBuilderAccess.Save is specified.
        '
        Dim myDomain As AppDomain = AppDomain.CurrentDomain
        Dim myAsmName As New AssemblyName("GenericEmitExample1")
        Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
            myAsmName, _
            AssemblyBuilderAccess.RunAndSave)

        ' An assembly is made up of executable modules. For a single-
        ' module assembly, the module name and file name are the same 
        ' as the assembly name. 
        '
        Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
            myAsmName.Name, _
            myAsmName.Name & ".dll")

        ' Get type objects for the base class trivial interfaces to
        ' be used as constraints.
        '
        Dim baseType As Type = GetType(ExampleBase)
        Dim interfaceA As Type = GetType(IExampleA)
        Dim interfaceB As Type = GetType(IExampleB)

        ' Define the sample type.
        '
        Dim myType As TypeBuilder = myModule.DefineType( _
            "Sample", _
            TypeAttributes.Public)

        Console.WriteLine("Type 'Sample' is generic: {0}", _
            myType.IsGenericType)

        ' Define type parameters for the type. Until you do this, 
        ' the type is not generic, as the preceding and following 
        ' WriteLine statements show. The type parameter names are
        ' specified as an array of strings. To make the code
        ' easier to read, each GenericTypeParameterBuilder is placed
        ' in a variable with the same name as the type parameter.
        ' 
        Dim typeParamNames() As String = {"TFirst", "TSecond"}
        Dim typeParams() As GenericTypeParameterBuilder = _
            myType.DefineGenericParameters(typeParamNames)

        Dim TFirst As GenericTypeParameterBuilder = typeParams(0)
        Dim TSecond As GenericTypeParameterBuilder = typeParams(1)

        Console.WriteLine("Type 'Sample' is generic: {0}", _
            myType.IsGenericType)

        ' Apply constraints to the type parameters.
        '
        ' A type that is substituted for the first parameter, TFirst,
        ' must be a reference type and must have a parameterless
        ' constructor.
        TFirst.SetGenericParameterAttributes( _
            GenericParameterAttributes.DefaultConstructorConstraint _
            Or GenericParameterAttributes.ReferenceTypeConstraint)

        ' A type that is substituted for the second type
        ' parameter must implement IExampleA and IExampleB, and
        ' inherit from the trivial test class ExampleBase. The
        ' interface constraints are specified as an array 
        ' containing the interface types.
        TSecond.SetBaseTypeConstraint(baseType)
        Dim interfaceTypes() As Type = {interfaceA, interfaceB}
        TSecond.SetInterfaceConstraints(interfaceTypes)

        ' The following code adds a private field named ExampleField,
        ' of type TFirst.
        Dim exField As FieldBuilder = _
            myType.DefineField("ExampleField", TFirst, _
                FieldAttributes.Private)

        ' Define a Shared method that takes an array of TFirst and 
        ' returns a List(Of TFirst) containing all the elements of 
        ' the array. To define this method it is necessary to create
        ' the type List(Of TFirst) by calling MakeGenericType on the
        ' generic type definition, List(Of T). (The T is omitted with
        ' the GetType operator when you get the generic type 
        ' definition.) The parameter type is created by using the
        ' MakeArrayType method. 
        '
        Dim listOf As Type = GetType(List(Of ))
        Dim listOfTFirst As Type = listOf.MakeGenericType(TFirst)
        Dim mParamTypes() As Type = { TFirst.MakeArrayType() }

        Dim exMethod As MethodBuilder = _
            myType.DefineMethod("ExampleMethod", _
                MethodAttributes.Public Or MethodAttributes.Static, _
                listOfTFirst, _
                mParamTypes)

        ' Emit the method body. 
        ' The method body consists of just three opcodes, to load 
        ' the input array onto the execution stack, to call the 
        ' List(Of TFirst) constructor that takes IEnumerable(Of TFirst),
        ' which does all the work of putting the input elements into
        ' the list, and to return, leaving the list on the stack. The
        ' hard work is getting the constructor.
        ' 
        ' The GetConstructor method is not supported on a 
        ' GenericTypeParameterBuilder, so it is not possible to get 
        ' the constructor of List(Of TFirst) directly. There are two
        ' steps, first getting the constructor of List(Of T) and then
        ' calling a method that converts it to the corresponding 
        ' constructor of List(Of TFirst).
        '
        ' The constructor needed here is the one that takes an
        ' IEnumerable(Of T). Note, however, that this is not the 
        ' generic type definition of IEnumerable(Of T); instead, the
        ' T from List(Of T) must be substituted for the T of 
        ' IEnumerable(Of T). (This seems confusing only because both
        ' types have type parameters named T. That is why this example
        ' uses the somewhat silly names TFirst and TSecond.) To get
        ' the type of the constructor argument, take the generic
        ' type definition IEnumerable(Of T) (expressed as 
        ' IEnumerable(Of ) when you use the GetType operator) and 
        ' call MakeGenericType with the first generic type parameter
        ' of List(Of T). The constructor argument list must be passed
        ' as an array, with just one argument in this case.
        ' 
        ' Now it is possible to get the constructor of List(Of T),
        ' using GetConstructor on the generic type definition. To get
        ' the constructor of List(Of TFirst), pass List(Of TFirst) and
        ' the constructor from List(Of T) to the static
        ' TypeBuilder.GetConstructor method.
        '
        Dim ilgen As ILGenerator = exMethod.GetILGenerator()

        Dim ienumOf As Type = GetType(IEnumerable(Of ))
        Dim listOfTParams() As Type = listOf.GetGenericArguments()
        Dim TfromListOf As Type = listOfTParams(0)
        Dim ienumOfT As Type = ienumOf.MakeGenericType(TfromListOf)
        Dim ctorArgs() As Type = { ienumOfT }

        Dim ctorPrep As ConstructorInfo = _
            listOf.GetConstructor(ctorArgs)
        Dim ctor As ConstructorInfo = _
            TypeBuilder.GetConstructor(listOfTFirst, ctorPrep)

        ilgen.Emit(OpCodes.Ldarg_0)
        ilgen.Emit(OpCodes.Newobj, ctor)
        ilgen.Emit(OpCodes.Ret)

        ' Create the type and save the assembly. 
        Dim finished As Type = myType.CreateType()
        myAssembly.Save(myAsmName.Name & ".dll")

        ' Invoke the method.
        ' ExampleMethod is not generic, but the type it belongs to is
        ' generic, so in order to get a MethodInfo that can be invoked
        ' it is necessary to create a constructed type. The Example 
        ' class satisfies the constraints on TFirst, because it is a 
        ' reference type and has a default constructor. In order to
        ' have a class that satisfies the constraints on TSecond, 
        ' this code example defines the ExampleDerived type. These
        ' two types are passed to MakeGenericMethod to create the
        ' constructed type.
        '
        Dim typeArgs() As Type = _
            { GetType(Example), GetType(ExampleDerived) }
        Dim constructed As Type = finished.MakeGenericType(typeArgs)
        Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")

        ' Create an array of Example objects, as input to the generic
        ' method. This array must be passed as the only element of an 
        ' array of arguments. The first argument of Invoke is 
        ' Nothing, because ExampleMethod is Shared. Display the count
        ' on the resulting List(Of Example).
        ' 
        Dim input() As Example = { New Example(), New Example() }
        Dim arguments() As Object = { input }

        Dim listX As List(Of Example) = mi.Invoke(Nothing, arguments)

        Console.WriteLine(vbLf & _
            "There are {0} elements in the List(Of Example).", _
            listX.Count _ 
        )

        DisplayGenericParameters(finished)
    End Sub

    Private Shared Sub DisplayGenericParameters(ByVal t As Type)

        If Not t.IsGenericType Then
            Console.WriteLine("Type '{0}' is not generic.")
            Return
        End If
        If Not t.IsGenericTypeDefinition Then _
            t = t.GetGenericTypeDefinition()

        Dim typeParameters() As Type = t.GetGenericArguments()
        Console.WriteLine(vbCrLf & _
            "Listing {0} type parameters for type '{1}'.", _
            typeParameters.Length, t)

        For Each tParam As Type In typeParameters

            Console.WriteLine(vbCrLf & "Type parameter {0}:", _
                tParam.ToString())

            For Each c As Type In tParam.GetGenericParameterConstraints()
                If c.IsInterface Then
                    Console.WriteLine("    Interface constraint: {0}", c)
                Else
                    Console.WriteLine("    Base type constraint: {0}", c)
                End If
            Next 

            ListConstraintAttributes(tParam)
        Next tParam
    End Sub

    ' List the constraint flags. The GenericParameterAttributes
    ' enumeration contains two sets of attributes, variance and
    ' constraints. For this example, only constraints are used.
    '
    Private Shared Sub ListConstraintAttributes(ByVal t As Type)

        ' Mask off the constraint flags. 
        Dim constraints As GenericParameterAttributes = _
            t.GenericParameterAttributes And _
            GenericParameterAttributes.SpecialConstraintMask

        If (constraints And GenericParameterAttributes.ReferenceTypeConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    ReferenceTypeConstraint")

        If (constraints And GenericParameterAttributes.NotNullableValueTypeConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    NotNullableValueTypeConstraint")

        If (constraints And GenericParameterAttributes.DefaultConstructorConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    DefaultConstructorConstraint")

    End Sub 

End Class

' This code example produces the following output:
'
'Type 'Sample' is generic: False
'Type 'Sample' is generic: True
'
'There are 2 elements in the List(Of Example).
'
'Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.
'
'Type parameter TFirst:
'    ReferenceTypeConstraint
'    DefaultConstructorConstraint
'
'Type parameter TSecond:
'    Interface constraint: IExampleA
'    Interface constraint: IExampleB
'    Base type constraint: ExampleBase
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

// Define a trivial base class and two trivial interfaces 
// to use when demonstrating constraints.
//
public class ExampleBase {}

public interface IExampleA {}

public interface IExampleB {}

// Define a trivial type that can substitute for type parameter 
// TSecond.
//
public class ExampleDerived : ExampleBase, IExampleA, IExampleB {}


public class Example
{
    public static void Main()
    {
        // Define a dynamic assembly to contain the sample type. The
        // assembly will not be run, but only saved to disk, so
        // AssemblyBuilderAccess.Save is specified.
        //
        AppDomain myDomain = AppDomain.CurrentDomain;
        AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
        AssemblyBuilder myAssembly = 
            myDomain.DefineDynamicAssembly(myAsmName, 
                AssemblyBuilderAccess.RunAndSave);

        // An assembly is made up of executable modules. For a single-
        // module assembly, the module name and file name are the same 
        // as the assembly name. 
        //
        ModuleBuilder myModule = 
            myAssembly.DefineDynamicModule(myAsmName.Name, 
               myAsmName.Name + ".dll");

        // Get type objects for the base class trivial interfaces to
        // be used as constraints.
        //
        Type baseType = typeof(ExampleBase);
        Type interfaceA = typeof(IExampleA);
        Type interfaceB = typeof(IExampleB);

        // Define the sample type.
        //
        TypeBuilder myType = 
            myModule.DefineType("Sample", TypeAttributes.Public);

        Console.WriteLine("Type 'Sample' is generic: {0}", 
            myType.IsGenericType);

        // Define type parameters for the type. Until you do this, 
        // the type is not generic, as the preceding and following 
        // WriteLine statements show. The type parameter names are
        // specified as an array of strings. To make the code
        // easier to read, each GenericTypeParameterBuilder is placed
        // in a variable with the same name as the type parameter.
        // 
        string[] typeParamNames = {"TFirst", "TSecond"};
        GenericTypeParameterBuilder[] typeParams = 
            myType.DefineGenericParameters(typeParamNames);

        GenericTypeParameterBuilder TFirst = typeParams[0];
        GenericTypeParameterBuilder TSecond = typeParams[1];

        Console.WriteLine("Type 'Sample' is generic: {0}", 
            myType.IsGenericType);

        // Apply constraints to the type parameters.
        //
        // A type that is substituted for the first parameter, TFirst,
        // must be a reference type and must have a parameterless
        // constructor.
        TFirst.SetGenericParameterAttributes(
            GenericParameterAttributes.DefaultConstructorConstraint |
            GenericParameterAttributes.ReferenceTypeConstraint);

        // A type that is substituted for the second type
        // parameter must implement IExampleA and IExampleB, and
        // inherit from the trivial test class ExampleBase. The
        // interface constraints are specified as an array 
        // containing the interface types.
        TSecond.SetBaseTypeConstraint(baseType);
        Type[] interfaceTypes = {interfaceA, interfaceB};
        TSecond.SetInterfaceConstraints(interfaceTypes);

        // The following code adds a private field named ExampleField,
        // of type TFirst.
        FieldBuilder exField = 
            myType.DefineField("ExampleField", TFirst, 
                FieldAttributes.Private);

        // Define a static method that takes an array of TFirst and 
        // returns a List<TFirst> containing all the elements of 
        // the array. To define this method it is necessary to create
        // the type List<TFirst> by calling MakeGenericType on the
        // generic type definition, List<T>. (The T is omitted with
        // the typeof operator when you get the generic type 
        // definition.) The parameter type is created by using the
        // MakeArrayType method. 
        //
        Type listOf = typeof(List<>);
        Type listOfTFirst = listOf.MakeGenericType(TFirst);
        Type[] mParamTypes = {TFirst.MakeArrayType()};

        MethodBuilder exMethod = 
            myType.DefineMethod("ExampleMethod", 
                MethodAttributes.Public | MethodAttributes.Static, 
                listOfTFirst, 
                mParamTypes);

        // Emit the method body. 
        // The method body consists of just three opcodes, to load 
        // the input array onto the execution stack, to call the 
        // List<TFirst> constructor that takes IEnumerable<TFirst>,
        // which does all the work of putting the input elements into
        // the list, and to return, leaving the list on the stack. The
        // hard work is getting the constructor.
        // 
        // The GetConstructor method is not supported on a 
        // GenericTypeParameterBuilder, so it is not possible to get 
        // the constructor of List<TFirst> directly. There are two
        // steps, first getting the constructor of List<T> and then
        // calling a method that converts it to the corresponding 
        // constructor of List<TFirst>.
        //
        // The constructor needed here is the one that takes an
        // IEnumerable<T>. Note, however, that this is not the 
        // generic type definition of IEnumerable<T>; instead, the
        // T from List<T> must be substituted for the T of 
        // IEnumerable<T>. (This seems confusing only because both
        // types have type parameters named T. That is why this example
        // uses the somewhat silly names TFirst and TSecond.) To get
        // the type of the constructor argument, take the generic
        // type definition IEnumerable<T> (expressed as 
        // IEnumerable<> when you use the typeof operator) and 
        // call MakeGenericType with the first generic type parameter
        // of List<T>. The constructor argument list must be passed
        // as an array, with just one argument in this case.
        // 
        // Now it is possible to get the constructor of List<T>,
        // using GetConstructor on the generic type definition. To get
        // the constructor of List<TFirst>, pass List<TFirst> and
        // the constructor from List<T> to the static
        // TypeBuilder.GetConstructor method.
        //
        ILGenerator ilgen = exMethod.GetILGenerator();

        Type ienumOf = typeof(IEnumerable<>);
        Type TfromListOf = listOf.GetGenericArguments()[0];
        Type ienumOfT = ienumOf.MakeGenericType(TfromListOf);
        Type[] ctorArgs = {ienumOfT};

        ConstructorInfo ctorPrep = listOf.GetConstructor(ctorArgs);
        ConstructorInfo ctor = 
            TypeBuilder.GetConstructor(listOfTFirst, ctorPrep);

        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Newobj, ctor);
        ilgen.Emit(OpCodes.Ret);

        // Create the type and save the assembly. 
        Type finished = myType.CreateType();
        myAssembly.Save(myAsmName.Name+".dll");

        // Invoke the method.
        // ExampleMethod is not generic, but the type it belongs to is
        // generic, so in order to get a MethodInfo that can be invoked
        // it is necessary to create a constructed type. The Example 
        // class satisfies the constraints on TFirst, because it is a 
        // reference type and has a default constructor. In order to
        // have a class that satisfies the constraints on TSecond, 
        // this code example defines the ExampleDerived type. These
        // two types are passed to MakeGenericMethod to create the
        // constructed type.
        //
        Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
        Type constructed = finished.MakeGenericType(typeArgs);
        MethodInfo mi = constructed.GetMethod("ExampleMethod");

        // Create an array of Example objects, as input to the generic
        // method. This array must be passed as the only element of an 
        // array of arguments. The first argument of Invoke is 
        // null, because ExampleMethod is static. Display the count
        // on the resulting List<Example>.
        // 
        Example[] input = {new Example(), new Example()};
        object[] arguments = {input};

        List<Example> listX = 
            (List<Example>) mi.Invoke(null, arguments);

        Console.WriteLine(
            "\nThere are {0} elements in the List<Example>.", 
            listX.Count);

        DisplayGenericParameters(finished);
    }

    private static void DisplayGenericParameters(Type t)
    {
        if (!t.IsGenericType)
        {
            Console.WriteLine("Type '{0}' is not generic.");
            return;
        }
        if (!t.IsGenericTypeDefinition) 
        {
            t = t.GetGenericTypeDefinition();
        }

        Type[] typeParameters = t.GetGenericArguments();
        Console.WriteLine("\nListing {0} type parameters for type '{1}'.",
            typeParameters.Length, t);

        foreach( Type tParam in typeParameters )
        {
            Console.WriteLine("\r\nType parameter {0}:", tParam.ToString());

            foreach( Type c in tParam.GetGenericParameterConstraints() )
            {
                if (c.IsInterface)
                {
                    Console.WriteLine("    Interface constraint: {0}", c);
                }
                else
                {
                    Console.WriteLine("    Base type constraint: {0}", c);
                }
            }

            ListConstraintAttributes(tParam);
        }
    }

    // List the constraint flags. The GenericParameterAttributes
    // enumeration contains two sets of attributes, variance and
    // constraints. For this example, only constraints are used.
    //
    private static void ListConstraintAttributes(Type t)
    {
        // Mask off the constraint flags. 
        GenericParameterAttributes constraints = 
            t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;

        if ((constraints & GenericParameterAttributes.ReferenceTypeConstraint)
            != GenericParameterAttributes.None) 
        {
            Console.WriteLine("    ReferenceTypeConstraint");
        }

        if ((constraints & GenericParameterAttributes.NotNullableValueTypeConstraint)
            != GenericParameterAttributes.None) 
        {
            Console.WriteLine("    NotNullableValueTypeConstraint");
        }

        if ((constraints & GenericParameterAttributes.DefaultConstructorConstraint)
            !=GenericParameterAttributes.None) 
        {
            Console.WriteLine("    DefaultConstructorConstraint");
        }
    }
}

/* This code example produces the following output:

Type 'Sample' is generic: False
Type 'Sample' is generic: True

There are 2 elements in the List<Example>.

Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.

Type parameter TFirst:
    ReferenceTypeConstraint
    DefaultConstructorConstraint

Type parameter TSecond:
    Interface constraint: IExampleA
    Interface constraint: IExampleB
    Base type constraint: ExampleBase
 */
using namespace System;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
using namespace System::Collections::Generic;

// Dummy class to satisfy TFirst constraints.
//
public ref class Example {};

// Define a trivial base class and two trivial interfaces 
// to use when demonstrating constraints.
//
public ref class ExampleBase {};
public interface class IExampleA {};
public interface class IExampleB {};

// Define a trivial type that can substitute for type parameter 
// TSecond.
//
public ref class ExampleDerived : ExampleBase, IExampleA, IExampleB {};

// List the constraint flags. The GenericParameterAttributes
// enumeration contains two sets of attributes, variance and
// constraints. For this example, only constraints are used.
//
static void ListConstraintAttributes( Type^ t )
{
   // Mask off the constraint flags. 
   GenericParameterAttributes constraints = 
       t->GenericParameterAttributes & 
       GenericParameterAttributes::SpecialConstraintMask;

   if ((constraints & GenericParameterAttributes::ReferenceTypeConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    ReferenceTypeConstraint");

   if ((constraints & GenericParameterAttributes::NotNullableValueTypeConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    NotNullableValueTypeConstraint");

   if ((constraints & GenericParameterAttributes::DefaultConstructorConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    DefaultConstructorConstraint");
}

static void DisplayGenericParameters( Type^ t )
{
   if (!t->IsGenericType)
   {
       Console::WriteLine( L"Type '{0}' is not generic." );
       return;
   }
   if (!t->IsGenericTypeDefinition)
       t = t->GetGenericTypeDefinition();

   array<Type^>^ typeParameters = t->GetGenericArguments();
   Console::WriteLine( L"\r\nListing {0} type parameters for type '{1}'.", 
       typeParameters->Length, t );

   for each ( Type^ tParam in typeParameters )
   {
       Console::WriteLine( L"\r\nType parameter {0}:", 
           tParam->ToString() );

       for each (Type^ c in tParam->GetGenericParameterConstraints())
       {
           if (c->IsInterface)
               Console::WriteLine( L"    Interface constraint: {0}", c);
           else
               Console::WriteLine( L"    Base type constraint: {0}", c);
       }
       ListConstraintAttributes(tParam);
   }
}

void main()
{
   // Define a dynamic assembly to contain the sample type. The
   // assembly will be run and also saved to disk, so
   // AssemblyBuilderAccess.RunAndSave is specified.
   //
   AppDomain^ myDomain = AppDomain::CurrentDomain;
   AssemblyName^ myAsmName = gcnew AssemblyName( L"GenericEmitExample1" );
   AssemblyBuilder^ myAssembly = myDomain->DefineDynamicAssembly( 
       myAsmName, AssemblyBuilderAccess::RunAndSave );

   // An assembly is made up of executable modules. For a single-
   // module assembly, the module name and file name are the same 
   // as the assembly name. 
   //
   ModuleBuilder^ myModule = myAssembly->DefineDynamicModule( 
       myAsmName->Name, String::Concat( myAsmName->Name, L".dll" ) );

   // Get type objects for the base class trivial interfaces to
   // be used as constraints.
   //
   Type^ baseType = ExampleBase::typeid; 
   Type^ interfaceA = IExampleA::typeid; 
   Type^ interfaceB = IExampleB::typeid;

   // Define the sample type.
   //
   TypeBuilder^ myType = myModule->DefineType( L"Sample", 
       TypeAttributes::Public );

   Console::WriteLine( L"Type 'Sample' is generic: {0}", 
       myType->IsGenericType );

   // Define type parameters for the type. Until you do this, 
   // the type is not generic, as the preceding and following 
   // WriteLine statements show. The type parameter names are
   // specified as an array of strings. To make the code
   // easier to read, each GenericTypeParameterBuilder is placed
   // in a variable with the same name as the type parameter.
   // 
   array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
   array<GenericTypeParameterBuilder^>^typeParams = 
       myType->DefineGenericParameters( typeParamNames );

   GenericTypeParameterBuilder^ TFirst = typeParams[0];
   GenericTypeParameterBuilder^ TSecond = typeParams[1];

   Console::WriteLine( L"Type 'Sample' is generic: {0}", 
       myType->IsGenericType );

   // Apply constraints to the type parameters.
   //
   // A type that is substituted for the first parameter, TFirst,
   // must be a reference type and must have a parameterless
   // constructor.
   TFirst->SetGenericParameterAttributes( 
       GenericParameterAttributes::DefaultConstructorConstraint | 
       GenericParameterAttributes::ReferenceTypeConstraint 
   );

   // A type that is substituted for the second type
   // parameter must implement IExampleA and IExampleB, and
   // inherit from the trivial test class ExampleBase. The
   // interface constraints are specified as an array
   // containing the interface types. 
   array<Type^>^interfaceTypes = { interfaceA, interfaceB };
   TSecond->SetInterfaceConstraints( interfaceTypes );
   TSecond->SetBaseTypeConstraint( baseType );

   // The following code adds a private field named ExampleField,
   // of type TFirst.
   FieldBuilder^ exField = 
       myType->DefineField("ExampleField", TFirst, 
           FieldAttributes::Private);

   // Define a static method that takes an array of TFirst and 
   // returns a List<TFirst> containing all the elements of 
   // the array. To define this method it is necessary to create
   // the type List<TFirst> by calling MakeGenericType on the
   // generic type definition, generic<T> List. 
   // The parameter type is created by using the
   // MakeArrayType method. 
   //
   Type^ listOf = List::typeid;
   Type^ listOfTFirst = listOf->MakeGenericType(TFirst);
   array<Type^>^ mParamTypes = { TFirst->MakeArrayType() };

   MethodBuilder^ exMethod = 
       myType->DefineMethod("ExampleMethod", 
           MethodAttributes::Public | MethodAttributes::Static, 
           listOfTFirst, 
           mParamTypes);

   // Emit the method body. 
   // The method body consists of just three opcodes, to load 
   // the input array onto the execution stack, to call the 
   // List<TFirst> constructor that takes IEnumerable<TFirst>,
   // which does all the work of putting the input elements into
   // the list, and to return, leaving the list on the stack. The
   // hard work is getting the constructor.
   // 
   // The GetConstructor method is not supported on a 
   // GenericTypeParameterBuilder, so it is not possible to get 
   // the constructor of List<TFirst> directly. There are two
   // steps, first getting the constructor of generic<T> List and then
   // calling a method that converts it to the corresponding 
   // constructor of List<TFirst>.
   //
   // The constructor needed here is the one that takes an
   // IEnumerable<T>. Note, however, that this is not the 
   // generic type definition of generic<T> IEnumerable; instead, the
   // T from generic<T> List must be substituted for the T of 
   // generic<T> IEnumerable. (This seems confusing only because both
   // types have type parameters named T. That is why this example
   // uses the somewhat silly names TFirst and TSecond.) To get
   // the type of the constructor argument, take the generic
   // type definition generic<T> IEnumerable and 
   // call MakeGenericType with the first generic type parameter
   // of generic<T> List. The constructor argument list must be passed
   // as an array, with just one argument in this case.
   // 
   // Now it is possible to get the constructor of generic<T> List,
   // using GetConstructor on the generic type definition. To get
   // the constructor of List<TFirst>, pass List<TFirst> and
   // the constructor from generic<T> List to the static
   // TypeBuilder.GetConstructor method.
   //
   ILGenerator^ ilgen = exMethod->GetILGenerator();

   Type^ ienumOf = IEnumerable::typeid;
   Type^ TfromListOf = listOf->GetGenericArguments()[0];
   Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf);
   array<Type^>^ ctorArgs = {ienumOfT};

   ConstructorInfo^ ctorPrep = listOf->GetConstructor(ctorArgs);
   ConstructorInfo^ ctor = 
       TypeBuilder::GetConstructor(listOfTFirst, ctorPrep);

   ilgen->Emit(OpCodes::Ldarg_0);
   ilgen->Emit(OpCodes::Newobj, ctor);
   ilgen->Emit(OpCodes::Ret);

   // Create the type and save the assembly. 
   Type^ finished = myType->CreateType();
   myAssembly->Save( String::Concat( myAsmName->Name, L".dll" ) );

   // Invoke the method.
   // ExampleMethod is not generic, but the type it belongs to is
   // generic, so in order to get a MethodInfo that can be invoked
   // it is necessary to create a constructed type. The Example 
   // class satisfies the constraints on TFirst, because it is a 
   // reference type and has a default constructor. In order to
   // have a class that satisfies the constraints on TSecond, 
   // this code example defines the ExampleDerived type. These
   // two types are passed to MakeGenericMethod to create the
   // constructed type.
   //
   array<Type^>^ typeArgs = 
       { Example::typeid, ExampleDerived::typeid };
   Type^ constructed = finished->MakeGenericType(typeArgs);
   MethodInfo^ mi = constructed->GetMethod("ExampleMethod");

   // Create an array of Example objects, as input to the generic
   // method. This array must be passed as the only element of an 
   // array of arguments. The first argument of Invoke is 
   // null, because ExampleMethod is static. Display the count
   // on the resulting List<Example>.
   // 
   array<Example^>^ input = { gcnew Example(), gcnew Example() };
   array<Object^>^ arguments = { input };

   List<Example^>^ listX = 
       (List<Example^>^) mi->Invoke(nullptr, arguments);

   Console::WriteLine(
       "\nThere are {0} elements in the List<Example>.", 
       listX->Count);

   DisplayGenericParameters(finished);
}

/* This code example produces the following output:

Type 'Sample' is generic: False
Type 'Sample' is generic: True

There are 2 elements in the List<Example>.

Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.

Type parameter TFirst:
    ReferenceTypeConstraint
    DefaultConstructorConstraint

Type parameter TSecond:
    Interface constraint: IExampleA
    Interface constraint: IExampleB
    Base type constraint: ExampleBase
 */

Compilando o código

  • O código contém C# using instruções (Imports em Visual Basic) necessário para compilação.

  • Nenhuma referência de assembly adicionais é necessária.

  • Compile o código na linha de comando usando o CSC. exe, Vbc. exe ou cl. Para compilar o código de Visual Studio, coloque-o em um modelo de projeto de aplicativo de console.

Consulte também

Referência

GenericTypeParameterBuilder

Conceitos

Cenários de Assembly dinâmico de emissão de reflexão

Outros recursos

Usando a reflexão emitir.