Vorgehensweise: Definieren eines generischen Typs mit Reflektionsausgabe

In diesem Artikel wird gezeigt, wie ein einfacher generischer Typ mit zwei Typparametern erstellt wird, wie Klasseneinschränkungen, Schnittstelleneinschränkungen und bestimmte Einschränkungen für Typparameter angewendet werden und wie Member erstellt werden, die die Typparameter der Klasse als Parametertypen und Rückgabetypen verwenden.

Wichtig

Eine Methode ist nicht generisch, weil sie zu einem generischen Typ gehört und die Typparameter dieses Typs verwendet. Eine Methode ist nur dann generisch, wenn sie über eine eigene Typparameterliste verfügt. Die meisten Methoden für generische Typen sind nicht generisch, so auch in diesem Beispiel. Ein Beispiel für die Ausgabe einer generischen Methode finden Sie unter Vorgehensweise: Definieren einer generischen Methode mit Reflektionsausgabe.

Definieren eines generischen Typs

  1. Definieren Sie eine dynamische Assembly mit der Bezeichnung GenericEmitExample1. In diesem Beispiel wird die Assembly ausgeführt und auf einem Datenträger gespeichert, daher wird AssemblyBuilderAccess.RunAndSave angegeben.

    AppDomain^ myDomain = AppDomain::CurrentDomain;
    AssemblyName^ myAsmName = gcnew AssemblyName( L"GenericEmitExample1" );
    AssemblyBuilder^ myAssembly = myDomain->DefineDynamicAssembly( 
        myAsmName, AssemblyBuilderAccess::RunAndSave );
    
    AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
    AssemblyBuilder myAssembly =
        myDomain.DefineDynamicAssembly(myAsmName,
            AssemblyBuilderAccess.RunAndSave);
    
    Dim myDomain As AppDomain = AppDomain.CurrentDomain
    Dim myAsmName As New AssemblyName("GenericEmitExample1")
    Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
        myAsmName, _
        AssemblyBuilderAccess.RunAndSave)
    
  2. Definieren Sie ein dynamisches Modul. Eine Assembly besteht aus ausführbaren Modulen. Der Modulname für eine Assembly mit nur einem Modul ist der gleiche wie der Assemblyname, und der Dateiname ist der Modulname plus eine Erweiterung.

    ModuleBuilder^ myModule = myAssembly->DefineDynamicModule( 
        myAsmName->Name, String::Concat( myAsmName->Name, L".dll" ) );
    
    ModuleBuilder myModule =
        myAssembly.DefineDynamicModule(myAsmName.Name,
           myAsmName.Name + ".dll");
    
    Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
        myAsmName.Name, _
        myAsmName.Name & ".dll")
    
  3. Definieren Sie eine Klasse. In diesem Beispiel heißt die Klasse Sample.

    TypeBuilder^ myType = myModule->DefineType( L"Sample", 
        TypeAttributes::Public );
    
    TypeBuilder myType =
        myModule.DefineType("Sample", TypeAttributes.Public);
    
    Dim myType As TypeBuilder = myModule.DefineType( _
        "Sample", _
        TypeAttributes.Public)
    
  4. Definieren Sie die generischen Typparameter von Sample, indem Sie ein Array von Zeichenfolgen, das die Namen der Parameter enthält, an die TypeBuilder.DefineGenericParameters-Methode übergeben. Dadurch wird die Klasse zu einem generischen Typ. Der Rückgabewert ist ein Array von GenericTypeParameterBuilder-Objekten, die Typparameter darstellen, die Sie in Ihrem ausgegebenen Code verwenden können.

    Im folgenden Code wird Sample zu einem generischen Typ mit den beiden Typparametern TFirst und TSecond gemacht. Damit der Code einfacher zu lesen ist, wird jede GenericTypeParameterBuilder in einer Variable mit dem gleichen Namen wie dem des Typparameters platziert.

    array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
    array<GenericTypeParameterBuilder^>^typeParams = 
        myType->DefineGenericParameters( typeParamNames );
    
    GenericTypeParameterBuilder^ TFirst = typeParams[0];
    GenericTypeParameterBuilder^ TSecond = typeParams[1];
    
    string[] typeParamNames = {"TFirst", "TSecond"};
    GenericTypeParameterBuilder[] typeParams =
        myType.DefineGenericParameters(typeParamNames);
    
    GenericTypeParameterBuilder TFirst = typeParams[0];
    GenericTypeParameterBuilder TSecond = typeParams[1];
    
    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)
    
  5. Fügen Sie den Typparametern besondere Einschränkungen hinzu. In diesem Beispiel wird der Typparameter TFirst auf Verweistypen sowie auf Typen beschränkt, die parameterlose Konstruktoren besitzen.

    TFirst->SetGenericParameterAttributes( 
        GenericParameterAttributes::DefaultConstructorConstraint | 
        GenericParameterAttributes::ReferenceTypeConstraint 
    );
    
    TFirst.SetGenericParameterAttributes(
        GenericParameterAttributes.DefaultConstructorConstraint |
        GenericParameterAttributes.ReferenceTypeConstraint);
    
    TFirst.SetGenericParameterAttributes( _
        GenericParameterAttributes.DefaultConstructorConstraint _
        Or GenericParameterAttributes.ReferenceTypeConstraint)
    
  6. Fügen Sie den Typparametern bei Bedarf Klassen- und Schnittstelleneinschränkungen hinzu. In diesem Beispiel wird der Typparameter TFirst auf Typen beschränkt, die von der Basisklasse abgeleitet wurden, die vom in der Variable baseType enthaltenen Type-Objekt dargestellt wird. Diese implementiert die Schnittstellen, dessen Typen in den Variablen interfaceA und interfaceB enthalten sind. Das Codebeispiel enthält die Deklaration und die Zuweisung dieser Variablen.

    array<Type^>^interfaceTypes = { interfaceA, interfaceB };
    TSecond->SetInterfaceConstraints( interfaceTypes );
    TSecond->SetBaseTypeConstraint( baseType );
    
    TSecond.SetBaseTypeConstraint(baseType);
    Type[] interfaceTypes = {interfaceA, interfaceB};
    TSecond.SetInterfaceConstraints(interfaceTypes);
    
    TSecond.SetBaseTypeConstraint(baseType)
    Dim interfaceTypes() As Type = {interfaceA, interfaceB}
    TSecond.SetInterfaceConstraints(interfaceTypes)
    
  7. Definieren Sie ein Feld. In diesem Beispiel wird der Typ des Felds durch den Typparameter TFirst angegeben. GenericTypeParameterBuilder wird von Type abgeleitet; Sie können also generische Typparameter immer dort benutzen, wo ein Typ verwendet werden kann.

    FieldBuilder^ exField = 
        myType->DefineField("ExampleField", TFirst, 
            FieldAttributes::Private);
    
    FieldBuilder exField =
        myType.DefineField("ExampleField", TFirst,
            FieldAttributes.Private);
    
    Dim exField As FieldBuilder = _
        myType.DefineField("ExampleField", TFirst, _
            FieldAttributes.Private)
    
  8. Definieren Sie eine Methode, die die Typparameter des generischen Typs verwendet. Beachten Sie, dass solche Methoden nicht generisch sind, es sei denn, sie weisen ihre eigenen Typparameterlisten auf. Der folgende Code definiert eine static-Methode (Shared in Visual Basic), die ein Array von TFirst akzeptiert und eine List<TFirst> (List(Of TFirst) in Visual Basic) zurückgibt, die alle Elemente des Arrays enthält. Um diese Methode zu definieren ist es erforderlich, den Typ List<TFirst> zu definieren, indem Sie MakeGenericType auf der generischen Typdefinition List<T> aufrufen. (Die T wird ausgelassen, wenn Sie den typeof-Operator (GetType in Visual Basic) verwenden, um die generische Typdefinition zu erhalten.) Der Parametertyp wird mithilfe der MakeArrayType-Methode erstellt.

    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);
    
    Type listOf = typeof(List<>);
    Type listOfTFirst = listOf.MakeGenericType(TFirst);
    Type[] mParamTypes = {TFirst.MakeArrayType()};
    
    MethodBuilder exMethod =
        myType.DefineMethod("ExampleMethod",
            MethodAttributes.Public | MethodAttributes.Static,
            listOfTFirst,
            mParamTypes);
    
    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)
    
  9. Geben Sie den Methodentext aus. Der Methodentext enthält drei Opcodes, die das Eingabearray auf den Stapel laden, den List<TFirst>-Konstruktor aufrufen und zurückgeben, der IEnumerable<TFirst> nimmt (was die Eingabeelemente in die Liste lädt), wobei das neue List<T>-Objekt auf dem Stapel gelassen wird. Der schwierige Teil beim Ausgeben dieses Codes ist es, den Konstruktor abzurufen.

    Die GetConstructor-Methode wird auf einer GenericTypeParameterBuilder nicht unterstützt, also ist es nicht möglich, den Konstruktor von List<TFirst> direkt zu erhalten. Zunächst ist es erforderlich, den Konstruktor der generischen Typdefinition List<T> abzurufen und dann eine Methode aufzurufen, die ihn in den entsprechenden Konstruktor von List<TFirst> konvertiert.

    Der Konstruktor, der für dieses Codebeispiel verwendet wird, nimmt eine IEnumerable<T>. Beachten Sie, dass dies jedoch nicht die generische Typdefinition der generischen Schnittstelle IEnumerable<T> ist. Der Typparameter T von List<T> muss stattdessen mit dem Typparameter T von IEnumerable<T> ersetzt werden. (Dies scheint nur daher verwirrend zu sein, weil beide Typen über Parameter mit der Bezeichnung T verfügen. Darum verwendet dieses Codebeispiel die Namen TFirst und TSecond.) Um den Typ des Konstruktorarguments zu erhalten, beginnen Sie mit der generischen Typdefinition IEnumerable<T>, und rufen Sie MakeGenericType mit dem ersten generischen Typparameter von List<T> auf. Die Liste des Konstruktorarguments muss als Array übergeben werden, wobei in diesem Fall nur ein Argument vorhanden ist.

    Hinweis

    Die generische Typdefinition wird als IEnumerable<> ausgedrückt, wenn Sie den typeof-Operator in C# verwenden. Alternativ als IEnumerable(Of ), wenn Sie den GetType-Operator in Visual Basic verwenden.

    Es ist nun möglich, den Konstruktor von List<T> abzurufen, indem Sie GetConstructor auf der generischen Typdefinition aufrufen. Um diesen Konstruktor in den entsprechenden Konstruktor von List<TFirst> zu konvertieren, übergeben Sie List<TFirst> und den Konstruktor von List<T> an die statische TypeBuilder.GetConstructor(Type, ConstructorInfo)-Methode.

    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);
    
    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);
    
    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)
    
  10. Erstellen Sie den Typ, und Speichern Sie die Datei.

    Type^ finished = myType->CreateType();
    myAssembly->Save( String::Concat( myAsmName->Name, L".dll" ) );
    
    Type finished = myType.CreateType();
    myAssembly.Save(myAsmName.Name+".dll");
    
    Dim finished As Type = myType.CreateType()
    myAssembly.Save(myAsmName.Name & ".dll")
    
  11. Rufen Sie die Methode auf. ExampleMethod ist nicht generisch, jedoch ist der zugehörige Typ generisch. Um also eine MethodInfo zu erhalten, die aufgerufen werden kann, ist es erforderlich, einen konstruierten Typ aus der Typdefinition von Sample zu erstellen. Der konstruierte Typ verwendet die Example-Klasse, die die Einschränkungen für TFirst erfüllt, da es sich hierbei um einen Verweistyp handelt und dieser über einen parameterlosen Standardkonstruktor verfügt, sowie die ExampleDerived-Klasse, die die Einschränkungen für TSecond erfüllt. (Den Code für ExampleDerived finden Sie im Beispielcodeabschnitt.) Diese beiden Typen werden an MakeGenericType übergeben, um den konstruierten Typ zu erstellen. Die MethodInfo wird dann mithilfe der GetMethod-Methode abgerufen.

    array<Type^>^ typeArgs = 
        { Example::typeid, ExampleDerived::typeid };
    Type^ constructed = finished->MakeGenericType(typeArgs);
    MethodInfo^ mi = constructed->GetMethod("ExampleMethod");
    
    Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
    Type constructed = finished.MakeGenericType(typeArgs);
    MethodInfo mi = constructed.GetMethod("ExampleMethod");
    
    Dim typeArgs() As Type = _
        {GetType(Example), GetType(ExampleDerived)}
    Dim constructed As Type = finished.MakeGenericType(typeArgs)
    Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
    
  12. Der folgende Code erstellt ein Array von Example-Objekten, platziert dieses Array in einem Array des Typs Object, der die Argumente der aufzurufenden Methode darstellt, und übergibt sie an die Invoke(Object, Object[])-Methode. Das erste Argument der Invoke-Methode ist ein NULL-Verweis, da die Methode static ist.

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

Beispiel

Das folgende Codebeispiel definiert eine Klasse namens Sample, zusammen mit einer Basisklasse und zwei Schnittstellen. Das Programm definiert zwei generische Typparameter für Sample und wandelt sie in einen generischen Typ um. Typparameter sind das Einzige, das einen Typ generisch macht. Das Programm stellt dies dar, indem eine Testnachricht vor und nach der Definition der Typparameter angezeigt wird.

Der Typparameter TSecond wird auch zur Darstellung von Klassen- und Schnittstelleneinschränkungen verwendet. Hierzu wird die Basisklasse und die Schnittstellen verwendet, und der Typparameter TFirst wird zur Darstellung besonderer Einschränkungen verwendet.

Das Codebeispiel definiert ein Feld und eine Methode, indem die Typparameter der Klasse für den Feldtyp und für den Parameter sowie den Rückgabetyp der Methode verwendet wird.

Nachdem die Sample-Klasse erstellt wurde, wird die Methode aufgerufen.

Das Programm enthält eine Methode, die Informationen über einen generischen Typ auflistet, sowie eine Methode, die die besonderen Einschränkungen für einen Typparameter auflistet. Diese Methoden werden zur Darstellung von Informationen über die abgeschlossene Sample-Klasse verwendet.

Das Programm speichert die abgeschlossenen Module als GenericEmitExample1.dll auf dem Datenträger, damit Sie sie mit Ildasm.exe (IL-Disassembler) öffnen und die CIL für die Sample-Klasse untersuchen können.

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

Weitere Informationen