Export (0) Print
Expand All
0 out of 1 rated this helpful - Rate this topic

How to: Define a Generic Type with Reflection Emit

This topic shows how to create a simple generic type with two type parameters, how to apply class constraints, interface constraints, and special constraints to the type parameters, and how to create members that use the type parameters of the class as parameter types and return types.

Important note Important Note:

A method is not generic just because it belongs to a generic type and uses the type parameters of that type. A method is generic only if it has its own type parameter list. Most methods on generic types are not generic, as in this example. For an example of emitting a generic method, see How to: Define a Generic Method with Reflection Emit.

To define a generic type

  1. Define a dynamic assembly named GenericEmitExample1. In Silverlight, a dynamic assembly is always created with AssemblyBuilderAccess.Run.

    
    AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
    AssemblyBuilder myAssembly =
        myDomain.DefineDynamicAssembly(myAsmName,
            AssemblyBuilderAccess.Run);
    
    
    
  2. Define a dynamic module. In Silverlight, an assembly contains only one executable module.

    
    ModuleBuilder myModule =
        myAssembly.DefineDynamicModule(myAsmName.Name);
    
    
    
  3. Define a class. In this example, the class is named Sample.

    
    TypeBuilder myType =
        myModule.DefineType("Sample", TypeAttributes.Public);
    
    
    
  4. Define the generic type parameters of Sample by passing an array of strings that contains the parameter names to the TypeBuilder.DefineGenericParameters method. This makes the class a generic type. The return value is an array of GenericTypeParameterBuilder objects that represent the type parameters, which can be used in your emitted code.

    In the following code, Sample becomes a generic type with type parameters TFirst and TSecond. To make the code easier to read, each GenericTypeParameterBuilder is placed in a variable that has the same name as the type parameter.

    
    string[] typeParamNames = { "TFirst", "TSecond" };
    GenericTypeParameterBuilder[] typeParams =
        myType.DefineGenericParameters(typeParamNames);
    
    GenericTypeParameterBuilder TFirst = typeParams[0];
    GenericTypeParameterBuilder TSecond = typeParams[1];
    
    
    
  5. Add special constraints to the type parameters. In this example, TFirst is constrained to be a reference type and to have a parameterless constructor.

    
    TFirst.SetGenericParameterAttributes(
        GenericParameterAttributes.DefaultConstructorConstraint |
        GenericParameterAttributes.ReferenceTypeConstraint);
    
    
    
  6. Optionally add class and interface constraints to the type parameters. In this example, type parameter TFirst is constrained to types that have the following characteristics:

    • They derive from the base class that is represented by the Type object contained in the variable baseType.

    • They implement the interfaces whose types are contained in the variables interfaceA and interfaceB.

    See the code example for the declaration and assignment of these variables.

    
    TSecond.SetBaseTypeConstraint(baseType);
    Type[] interfaceTypes = { interfaceA, interfaceB };
    TSecond.SetInterfaceConstraints(interfaceTypes);
    
    
    
  7. Define a field. In this example, the type of the field is specified by type parameter TFirst. GenericTypeParameterBuilder derives from Type, so you can use generic type parameters anywhere a type can be used.

    
    FieldBuilder exField =
        myType.DefineField("ExampleField", TFirst,
            FieldAttributes.Private);
    
    
    
  8. Define a method that uses the type parameters of the generic type. Note that such methods are not generic unless they have their own type parameter lists. The following code defines a static method (Shared in Visual Basic) that takes an array of TFirst and returns a List<TFirst> (List(Of TFirst) in Visual Basic) that contains all the elements of the array. To define this method, you must create the type List<TFirst> by calling the MakeGenericType method on the generic type definition, List<T>. The T is omitted when you use the typeof operator (GetType in Visual Basic) to 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);
    
    
    
  9. Emit the method body. The method body consists of three opcodes that load the input array onto the stack, call the List<TFirst> constructor that takes IEnumerable<TFirst> (which does all the work of putting the input elements into the list), and return (leaving the new List<T> object on the stack). The difficult part of emitting this code 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. You must first get the constructor of the generic type definition List<T>, and then call a method that converts it to the corresponding constructor of List<TFirst>.

    The constructor that is used for this code example takes an IEnumerable<T>. Note, however, that this is not the generic type definition of the IEnumerable<T> generic interface; instead, the type parameter T from List<T> must be substituted for the type parameter T of IEnumerable<T>. (This seems confusing only because both types have type parameters named T. That is why this code example uses the names TFirst and TSecond.) To get the type of the constructor argument, start with the generic type definition IEnumerable<T> 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.

    Note Note:

    The generic type definition is expressed as IEnumerable<> when you use the typeof operator in C#, or IEnumerable(Of ) when you use the GetType operator in Visual Basic.

    Now it is possible to get the constructor of List<T> by calling GetConstructor on the generic type definition. To convert this constructor to the corresponding constructor of List<TFirst>, pass List<TFirst> and the constructor from List<T> to the static TypeBuilder.GetConstructor(Type, ConstructorInfo) 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);
    
    
    
  10. Create the type.

    
    Type finished = myType.CreateType();
    
    
    
  11. Invoke the method. ExampleMethod is not generic, but the type it belongs to is generic. Therefore, to get a MethodInfo that can be invoked, you must create a constructed type from the type definition for Sample. The constructed type uses the Example and ExampleDerived classes. The Example class satisfies the constraints on TFirst because it is a reference type and has a default, parameterless constructor. The ExampleDerived class satisfies the constraints on TSecond. (The code for ExampleDerived can be found in the Example section later in this topic.) These two types are passed to MakeGenericType to create the constructed type. The MethodInfo is then obtained by using the GetMethod method.

    
    Type[] typeArgs = { typeof(Example), typeof(ExampleDerived) };
    Type constructed = finished.MakeGenericType(typeArgs);
    MethodInfo mi = constructed.GetMethod("ExampleMethod");
    
    
    
  12. The following code creates an array of Example objects, places that array in an array of type Object that represents the arguments of the method to be invoked, and passes them to the Invoke(Object, Object[]) method. The first argument of the Invoke method is a null reference because the method is static.

    
    Example[] input = { new Example(), new Example() };
    object[] arguments = { input };
    
    List<Example> listX =
        (List<Example>)mi.Invoke(null, arguments);
    
    outputBlock.Text += String.Format(
        "\nThere are {0} elements in the List<Example>.",
        listX.Count) + "\n";
    
    
    

The following example defines a class named Sample, a base class, and two interfaces. The program defines two generic type parameters for Sample, turning it into a generic type. Type parameters are the only thing that makes a type generic. The program shows this by displaying a test message before and after the definition of the type parameters.

The type parameter TSecond is used to demonstrate class and interface constraints, using the base class and interfaces, and the type parameter TFirst is used to demonstrate special constraints.

The code example defines a field and a method by using the type parameters of Sample for the field type, and for the parameter and return type of the method.

After the Sample class has been created, the method is invoked.

The program includes a method that lists information about a generic type, and a method that lists the special constraints on a type parameter. These methods are used to display information about the finished Sample class.


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
{
   // Make the output TextBlock available to all methods.
   private static System.Windows.Controls.TextBlock outputBlock;

   public static void Demo(System.Windows.Controls.TextBlock output)
   {
      outputBlock = output;

      // Define a dynamic assembly to contain the sample type. In Silverlight,
      // a dynamic assembly contains one module.
      //
      AppDomain myDomain = AppDomain.CurrentDomain;
      AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
      AssemblyBuilder myAssembly =
          myDomain.DefineDynamicAssembly(myAsmName,
              AssemblyBuilderAccess.Run);
      ModuleBuilder myModule =
          myAssembly.DefineDynamicModule(myAsmName.Name);

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

      outputBlock.Text += String.Format("Type 'Sample' is generic: {0}",
          myType.IsGenericType) + "\n";

      // 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];

      outputBlock.Text += String.Format("Type 'Sample' is generic: {0}",
          myType.IsGenericType) + "\n";

      // 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. 
      Type finished = myType.CreateType();

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

      outputBlock.Text += String.Format(
          "\nThere are {0} elements in the List<Example>.",
          listX.Count) + "\n";

      DisplayGenericParameters(finished);
   }

   private static void DisplayGenericParameters(Type t)
   {
      if (!t.IsGenericType)
      {
         outputBlock.Text += String.Format("Type '{0}' is not generic.") + "\n";
         return;
      }
      if (!t.IsGenericTypeDefinition)
      {
         t = t.GetGenericTypeDefinition();
      }

      Type[] typeParameters = t.GetGenericArguments();
      outputBlock.Text += String.Format("\nListing {0} type parameters for type '{1}'.",
          typeParameters.Length, t) + "\n";

      foreach (Type tParam in typeParameters)
      {
         outputBlock.Text += String.Format("\r\nType parameter {0}:", tParam.ToString()) + "\n";

         foreach (Type c in tParam.GetGenericParameterConstraints())
         {
            if (c.IsInterface)
            {
               outputBlock.Text += String.Format("    Interface constraint: {0}", c) + "\n";
            }
            else
            {
               outputBlock.Text += String.Format("    Base type constraint: {0}", c) + "\n";
            }
         }

         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)
      {
         outputBlock.Text += "    ReferenceTypeConstraint" + "\n";
      }

      if ((constraints & GenericParameterAttributes.NotNullableValueTypeConstraint)
          != GenericParameterAttributes.None)
      {
         outputBlock.Text += "    NotNullableValueTypeConstraint" + "\n";
      }

      if ((constraints & GenericParameterAttributes.DefaultConstructorConstraint)
          != GenericParameterAttributes.None)
      {
         outputBlock.Text += "    DefaultConstructorConstraint" + "\n";
      }
   }
}

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


  • The code contains the C# using statements (Imports in Visual Basic) necessary for compilation.

  • No additional assembly references are required.

  • The code contains a static (Shared in Visual Basic) Demo method that has one parameter, a TextBlock that is used to display the output. For instructions on building the code as part of a simple Silverlight-based application, see Building Examples That Use a Demo Method and a TextBlock Control. You can put all the code in this sample into one source file.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.