Exportar (0) Imprimir
Expandir Tudo
Este artigo foi traduzido por máquina. Coloque o ponteiro do mouse sobre as frases do artigo para ver o texto original. Mais informações.
Tradução
Original

Como: Definir um tipo genérico com reflexo emita

This Topic shows How to Criar a tipo genérico Simple with two Parameters tipo, Como aplicar classe Constraints, Constraints interface, and special Constraints to the Parameters tipo, and how to Criar members that use The parameters tipo of the classe as Types parâmetro and Return Types.

Observação importanteObservação importante:

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

Para definir um tipo genérico

  1. Definir a nomeado assembly dinâmico GenericEmitExample1.Neste exemplo, o conjunto de módulos (assembly) é executado e salvo no disco, para AssemblyBuilderAccess.RunAndSave for especificado.

    AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
    AssemblyBuilder myAssembly = 
        myDomain.DefineDynamicAssembly(myAsmName, 
            AssemblyBuilderAccess.RunAndSave);
    
    
    
  2. Definir a dinâmico módulo.An assembly is made up of Executável Modules.For a Simples - módulo assembly, the name módulo is the same as the name assembly, and the name arquivo is the name módulo Plus an extensão.

    ModuleBuilder myModule = 
        myAssembly.DefineDynamicModule(myAsmName.Name, 
           myAsmName.Name + ".dll");
    
    
    
  3. Definir a classe.Neste exemplo, a classe é denominada Sample.

    TypeBuilder myType = 
        myModule.DefineType("Sample", TypeAttributes.Public);
    
    
    
  4. Definir os parâmetros tipo genérico de Sample passando uma matriz de Cadeia de caracteres que contém os nomes dos parâmetros para o método TypeBuilder.DefineGenericParameters.Isso torna a classe de um tipo genérico.O valor de retorno é uma matriz de objetos GenericTypeParameterBuilder representando os parâmetros 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 facilitar a leitura de código, cada GenericTypeParameterBuilder é colocado em uma variável com o mesmo nome que o parâmetro de tipo.

    string[] typeParamNames = {"TFirst", "TSecond"};
    GenericTypeParameterBuilder[] typeParams = 
        myType.DefineGenericParameters(typeParamNames);
    
    GenericTypeParameterBuilder TFirst = typeParams[0];
    GenericTypeParameterBuilder TSecond = typeParams[1];
    
    
    
  5. Add Especial Constraints to the Parameters tipo.Neste exemplo, Tipo de parâmetro TFirst é restrito para tipos que possuem construtor sem parâmetros e para tipos de referência.

    TFirst.SetGenericParameterAttributes(
        GenericParameterAttributes.DefaultConstructorConstraint |
        GenericParameterAttributes.ReferenceTypeConstraint);
    
    
    
  6. Optionally adicionar classe and interface Constraints to the tipo Parâmetros.Neste exemplo, Tipo de parâmetro TFirst é restrita a tipos que derivar da classe base representado pelo objeto Type contido a variável baseType, e que implementam as interfaces cujos tipos estão contidos nas variáveis interfaceA e interfaceB.Consulte o exemplo de código para a declaração e atribuição dessas variáveis.

    TSecond.SetBaseTypeConstraint(baseType);
    Type[] interfaceTypes = {interfaceA, interfaceB};
    TSecond.SetInterfaceConstraints(interfaceTypes);
    
    
    
  7. Definir a campo.Neste exemplo, o tipo do campo é especificado por Tipo parâmetro TFirst. GenericTypeParameterBuilder deriva de Type, portanto, você pode utilizar parâmetros tipo genérico em qualquer lugar que um tipo pode ser usado.

    FieldBuilder exField = 
        myType.DefineField("ExampleField", TFirst, 
            FieldAttributes.Private);
    
    
    
  8. Define a método that uses the tipo Parâmetros of the tipo genérico.Anotação que esses métodos não genérico, a menos que tenham suas próprias listas parâmetro do tipo.The seguinte Código Defines a método static (Shared in Visual Basic) that Takes an matriz of TFirst and returns a List<TFirst> (List(Of TFirst) in Visual Basic) containing All the elements of the matriz.Para definir esse método, é necessário criar o tipo List<TFirst> chamando na definição de tipo genérico, MakeGenericTypeList<T>.(O T for omitido ao usar o operador typeof (GetType no Visual Basic) para obter a definição de tipo genérico.) O tipo de parâmetro é criado usando o método MakeArrayType.

    Type listOf = typeof(List<>);
    Type listOfTFirst = listOf.MakeGenericType(TFirst);
    Type[] mParamTypes = {TFirst.MakeArrayType()};
    
    MethodBuilder exMethod = 
        myType.DefineMethod("ExampleMethod", 
            MethodAttributes.Public | MethodAttributes.Static, 
            listOfTFirst, 
            mParamTypes);
    
    
    
  9. The método emit Mensagem.The corpo método consists of three OpCodes that the entrada carregar matriz onto the pilha, chamar the construtor List<TFirst> that Takes IEnumerable<TFirst> (which All the trabalho of the entrada putting elements into the Lista), and Return (leaving the New objeto List<T> on the pilha).A parte difícil de emissor esse código está recebendo o construtor.

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

    O construtor usado para esse exemplo de código utiliza um IEnumerable<T>.Anotação, however, that this not is the definição tipo genérico of the genérico IEnumerable<T> interface; Em vez disso, o tipo de parâmetro T de List<T> deve ser substituído para o tipo de parâmetro T de IEnumerable<T>.(Isso parece confuso apenas porque ambos os tipos tem parâmetros de tipo chamados T.That is por que esse exemplo de código usa os nomes TFirst e TSecond.) To get the tipo of the argumento construtor, Iniciar with the tipo genérico definição IEnumerable<T> and chamar MakeGenericType with the first parâmetro de tipo genérico of List<T>.The lista de argumentos construtor must be Passed as an matriz, com argumento one just in this maiúsculas e minúsculas.

    ObservaçãoObservação:

    A definição de tipo genérico é expresso como IEnumerable<> quando você usa o operador typeof em C#, ou IEnumerable(Of ) quando você usa o operador GetType no Visual Basic.

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

    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);
    
    
    
  10. Criar The and the .

    Type finished = myType.CreateType();
    myAssembly.Save(myAsmName.Name+".dll");
    
    
    
  11. Chame o método. ExampleMethod não é genérico, mas o tipo ele pertence é genérico, caso em ordem para get um MethodInfo que podem ser chamados-é necessário criar um tipo construído de definição de tipo para Sample.O tipo construído usa a classe Example , 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 a classe que satisfaça as restrições em ExampleDerivedTSecond.(O código de ExampleDerived pode ser encontrado na seção de código de exemplo.) Esses dois tipos são passados para MakeGenericType para criar o tipo construído.O MethodInfo , em seguida, é obtida usando o método GetMethod.

    Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
    Type constructed = finished.MakeGenericType(typeArgs);
    MethodInfo mi = constructed.GetMethod("ExampleMethod");
    
    
    
  12. O código a seguir cria uma matriz de objetos Example , coloca essa matriz em uma matriz do tipo Object representando os argumentos do método a ser chamado e transmite-los para o método Invoke(Object, Object[]).O primeiro argumento do método Invoke é uma referência nula porque o método é static.

    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);
    
    
    

O exemplo de código a seguir define uma classe denominada Sample, juntamente com uma classe base e duas interfaces.O programa define dois parâmetros de tipo genérico para Sample, transformando-lo em um tipo genérico.Digite parâmetros são a única coisa que torna um tipo genérico.The programa shows this by displaying a mensagem testar Antes e depois the definição of the Parameters tipo.

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

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

After the Sample classe has been Criado, the método is invoked.

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

O programa salva o módulo concluído No Disco como GenericEmitExample1.dll, portanto, você pode aberto com o Desassemblador do MSIL (ildasm.exe) e examine o MSIL para a classe Sample.

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
 */


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

  • Há referências do conjunto de módulos (assembly) adicionais são necessárias.

  • Compilar o Código na linha de comando usando Csc.exe, vbc.exe ou CL.exe.A compilação de Código em Visual Studio, local-lo em um modelo projeto de aplicativo console.

Contribuições da comunidade

ADICIONAR
A Microsoft está realizando uma pesquisa online para saber sua opinião sobre o site do MSDN. Se você optar por participar, a pesquisa online lhe será apresentada quando você sair do site do MSDN.

Deseja participar?
Mostrar:
© 2014 Microsoft