How to: Examine and Instantiate Generic Types with Reflection

Information about generic types is obtained in the same way as information about other types: by examining a Type object that represents the generic type. The principle difference is that a generic type has a list of Type objects representing its generic type parameters. The first procedure in this section examines generic types.

You can create a Type object that represents a constructed type by binding type arguments to the type parameters of a generic type definition. The second procedure demonstrates this.

To examine a generic type and its type parameters

  1. Get an instance of Type that represents the generic type. In the following code, the type is obtained using the C# typeof operator (GetType in Visual Basic, typeid in Visual C++). See the Type class topic for other ways to get a Type object. Note that in the rest of this procedure, the type is contained in a method parameter named t.

    Type^ d1 = Dictionary::typeid;
    
  2. Use the IsGenericType property to determine whether the type is generic, and use the IsGenericTypeDefinition property to determine whether the type is a generic type definition.

    Console::WriteLine("   Is this a generic type? {0}",
        t->IsGenericType);
    Console::WriteLine("   Is this a generic type definition? {0}",
        t->IsGenericTypeDefinition);
    
  3. Get an array that contains the generic type arguments, using the GetGenericArguments method.

    array<Type^>^ typeParameters = t->GetGenericArguments();
    
  4. For each type argument, determine whether it is a type parameter (for example, in a generic type definition) or a type that has been specified for a type parameter (for example, in a constructed type), using the IsGenericParameter property.

    Console::WriteLine("   List {0} type arguments:",
        typeParameters->Length);
    for each( Type^ tParam in typeParameters )
    {
        if (tParam->IsGenericParameter)
        {
            DisplayGenericParameter(tParam);
        }
        else
        {
            Console::WriteLine("      Type argument: {0}",
                tParam);
        }
    }
    
  5. In the type system, a generic type parameter is represented by an instance of Type, just as ordinary types are. The following code displays the name and parameter position of a Type object that represents a generic type parameter. The parameter position is trivial information here; it is of more interest when you are examining a type parameter that has been used as a type argument of another generic type.

    static void DisplayGenericParameter(Type^ tp)
    {
        Console::WriteLine("      Type parameter: {0} position {1}",
            tp->Name, tp->GenericParameterPosition);
    
  6. Determine the base type constraint and the interface constraints of a generic type parameter by using the GetGenericParameterConstraints method to obtain all the constraints in a single array. Constraints are not guaranteed to be in any particular order.

    Type^ classConstraint = nullptr;
    
    for each(Type^ iConstraint in tp->GetGenericParameterConstraints())
    {
        if (iConstraint->IsInterface)
        {
            Console::WriteLine("         Interface constraint: {0}",
                iConstraint);
        }
    }
    
    if (classConstraint != nullptr)
    {
        Console::WriteLine("         Base type constraint: {0}",
            tp->BaseType);
    }
    else
        Console::WriteLine("         Base type constraint: None");
    
  7. Use the GenericParameterAttributes property to discover the special constraints on a type parameter, such as requiring that it be a reference type. The property also includes values that represent variance, which you can mask off as shown in the following code.

    GenericParameterAttributes sConstraints =
        tp->GenericParameterAttributes &
        GenericParameterAttributes::SpecialConstraintMask;
    
  8. The special constraint attributes are flags, and the same flag (GenericParameterAttributes::None) that represents no special constraints also represents no covariance or contravariance. Thus, to test for either of these conditions you must use the appropriate mask. In this case, use GenericParameterAttributes::SpecialConstraintMask to isolate the special constraint flags.

    if (sConstraints == GenericParameterAttributes::None)
    {
        Console::WriteLine("         No special constraints.");
    }
    else
    {
        if (GenericParameterAttributes::None != (sConstraints &
            GenericParameterAttributes::DefaultConstructorConstraint))
        {
            Console::WriteLine("         Must have a parameterless constructor.");
        }
        if (GenericParameterAttributes::None != (sConstraints &
            GenericParameterAttributes::ReferenceTypeConstraint))
        {
            Console::WriteLine("         Must be a reference type.");
        }
        if (GenericParameterAttributes::None != (sConstraints &
            GenericParameterAttributes::NotNullableValueTypeConstraint))
        {
            Console::WriteLine("         Must be a non-nullable value type.");
        }
    }
    

A generic type is like a template. You cannot create instances of it unless you specify real types for its generic type parameters. To do this at run time, using reflection, requires the MakeGenericType method.

To construct an instance of a generic type

  1. Get a Type object that represents the generic type. The following code gets the generic type Dictionary<TKey, TValue> in two different ways: by using the Type::GetType(String) method overload with a string describing the type, and by calling the GetGenericTypeDefinition method on the constructed type Dictionary<String, Example> (Dictionary(Of String, Example) in Visual Basic). The MakeGenericType method requires a generic type definition.

    // Use the typeid keyword to create the generic type 
    // definition directly.
    Type^ d1 = Dictionary::typeid;
    
    // You can also obtain the generic type definition from a 
    // constructed class. In this case, the constructed class 
    // is a dictionary of Example objects, with String keys.
    Dictionary<String^, Example^>^ d2 = gcnew Dictionary<String^, Example^>();
    // Get a Type object that represents the constructed type, 
    // and from that get the generic type definition. The 
    // variables d1 and d4 contain the same type.
    Type^ d3 = d2->GetType();
    Type^ d4 = d3->GetGenericTypeDefinition();
    
  2. Construct an array of type arguments to substitute for the type parameters. The array must contain the correct number of Type objects, in the same order as they appear in the type parameter list. In this case, the key (first type parameter) is of type String, and the values in the dictionary are instances of a class named Example.

    array<Type^>^ typeArgs = {String::typeid, Example::typeid};
    
  3. Call the MakeGenericType method to bind the type arguments to the type parameters and construct the type.

    Type^ constructed = d1->MakeGenericType(typeArgs);
    
  4. Use the CreateInstance(Type) method overload to create an object of the constructed type. The following code stores two instances of the Example class in the resulting Dictionary<String, Example> object.

    Object^ o = Activator::CreateInstance(constructed);
    

The following code example defines a DisplayGenericType method to examine the generic type definitions and constructed types used in the code and display their information. The DisplayGenericType method shows how to use the IsGenericType, IsGenericParameter, and GenericParameterPosition properties and the GetGenericArguments method.

The example also defines a DisplayGenericParameter method to examine a generic type parameter and display its constraints.

The code example defines a set of test types, including a generic type that illustrates type parameter constraints, and shows how to display information about these types.

The example constructs a type from the Dictionary<TKey, TValue> class by creating an array of type arguments and calling the MakeGenericType method. The program compares the Type object constructed using MakeGenericType with a Type object obtained using typeof (GetType in Visual Basic), demonstrating that they are the same. Similarly, the program uses the GetGenericTypeDefinition method to obtain the generic type definition of the constructed type, and compares it to the Type object representing the Dictionary<TKey, TValue> class.

using namespace System;
using namespace System::Reflection;
using namespace System::Collections::Generic;
using namespace System::Security::Permissions;

// Define an example interface. 
public interface class ITestArgument {};

// Define an example base class. 
public ref class TestBase {};

// Define a generic class with one parameter. The parameter 
// has three constraints: It must inherit TestBase, it must 
// implement ITestArgument, and it must have a parameterless 
// constructor. 

generic<class T>
    where T : TestBase, ITestArgument, gcnew()
public ref class Test {};

// Define a class that meets the constraints on the type 
// parameter of class Test. 
public ref class TestArgument : TestBase, ITestArgument
{
public:
    TestArgument() {}
};

public ref class Example
{
    // The following method displays information about a generic 
    // type. 
private:
    static void DisplayGenericType(Type^ t)
    {
        Console::WriteLine("\r\n {0}", t);
        Console::WriteLine("   Is this a generic type? {0}",
            t->IsGenericType);
        Console::WriteLine("   Is this a generic type definition? {0}",
            t->IsGenericTypeDefinition);

        // Get the generic type parameters or type arguments. 
        array<Type^>^ typeParameters = t->GetGenericArguments();

        Console::WriteLine("   List {0} type arguments:",
            typeParameters->Length);
        for each( Type^ tParam in typeParameters )
        {
            if (tParam->IsGenericParameter)
            {
                DisplayGenericParameter(tParam);
            }
            else
            {
                Console::WriteLine("      Type argument: {0}",
                    tParam);
            }
        }
    }

    // The following method displays information about a generic 
    // type parameter. Generic type parameters are represented by 
    // instances of System.Type, just like ordinary types. 
    static void DisplayGenericParameter(Type^ tp)
    {
        Console::WriteLine("      Type parameter: {0} position {1}",
            tp->Name, tp->GenericParameterPosition);

        Type^ classConstraint = nullptr;

        for each(Type^ iConstraint in tp->GetGenericParameterConstraints())
        {
            if (iConstraint->IsInterface)
            {
                Console::WriteLine("         Interface constraint: {0}",
                    iConstraint);
            }
        }

        if (classConstraint != nullptr)
        {
            Console::WriteLine("         Base type constraint: {0}",
                tp->BaseType);
        }
        else
            Console::WriteLine("         Base type constraint: None");

        GenericParameterAttributes sConstraints =
            tp->GenericParameterAttributes &
            GenericParameterAttributes::SpecialConstraintMask;

        if (sConstraints == GenericParameterAttributes::None)
        {
            Console::WriteLine("         No special constraints.");
        }
        else
        {
            if (GenericParameterAttributes::None != (sConstraints &
                GenericParameterAttributes::DefaultConstructorConstraint))
            {
                Console::WriteLine("         Must have a parameterless constructor.");
            }
            if (GenericParameterAttributes::None != (sConstraints &
                GenericParameterAttributes::ReferenceTypeConstraint))
            {
                Console::WriteLine("         Must be a reference type.");
            }
            if (GenericParameterAttributes::None != (sConstraints &
                GenericParameterAttributes::NotNullableValueTypeConstraint))
            {
                Console::WriteLine("         Must be a non-nullable value type.");
            }
        }
    }

public:
    [PermissionSetAttribute(SecurityAction::Demand, Name="FullTrust")]
    static void Main()
    {
        // Two ways to get a Type object that represents the generic 
        // type definition of the Dictionary class. 
        // 
        // Use the typeid keyword to create the generic type 
        // definition directly.
        Type^ d1 = Dictionary::typeid;

        // You can also obtain the generic type definition from a 
        // constructed class. In this case, the constructed class 
        // is a dictionary of Example objects, with String keys.
        Dictionary<String^, Example^>^ d2 = gcnew Dictionary<String^, Example^>();
        // Get a Type object that represents the constructed type, 
        // and from that get the generic type definition. The 
        // variables d1 and d4 contain the same type.
        Type^ d3 = d2->GetType();
        Type^ d4 = d3->GetGenericTypeDefinition();

        // Display information for the generic type definition, and 
        // for the constructed type Dictionary<String, Example>.
        DisplayGenericType(d1);
        DisplayGenericType(d2->GetType());

        // Construct an array of type arguments to substitute for 
        // the type parameters of the generic Dictionary class. 
        // The array must contain the correct number of types, in 
        // the same order that they appear in the type parameter 
        // list of Dictionary. The key (first type parameter) 
        // is of type string, and the type to be contained in the 
        // dictionary is Example. 
        array<Type^>^ typeArgs = {String::typeid, Example::typeid};

        // Construct the type Dictionary<String, Example>.
        Type^ constructed = d1->MakeGenericType(typeArgs);

        DisplayGenericType(constructed);

        Object^ o = Activator::CreateInstance(constructed);

        Console::WriteLine("\r\nCompare types obtained by different methods:");
        Console::WriteLine("   Are the constructed types equal? {0}",
            (d2->GetType()==constructed));
        Console::WriteLine("   Are the generic definitions equal? {0}",
            (d1==constructed->GetGenericTypeDefinition()));

        // Demonstrate the DisplayGenericType and 
        // DisplayGenericParameter methods with the Test class 
        // defined above. This shows base, interface, and special 
        // constraints.
        DisplayGenericType(Test::typeid);
    }
};

int main()
{
    Example::Main();
}

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

  • No additional assembly references are required.

  • Compile the code at the command line using csc.exe, vbc.exe, or cl.exe. To compile the code in Visual Studio, place it in a console application project template.

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft