Partager via


How to: Examine and Instantiate Generic Types with Reflection

Microsoft Silverlight will reach end of support after October 2021. Learn more.

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 principal difference is that a generic type has a list of Type objects that represent its generic type parameters. The first procedure in this topic 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 by 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.

    Dim d1 As Type = GetType(Dictionary(Of ,))
    
    Type d1 = typeof(Dictionary<,>);
    
  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.

    output.Text &= "   Is this a generic type? " _ 
        & t.IsGenericType & vbCrLf
    output.Text &= "   Is this a generic type definition? " _ 
        & t.IsGenericTypeDefinition & vbCrLf
    
    output.Text += String.Format("   Is this a generic type? {0}\r\n",
        t.IsGenericType);
    output.Text += String.Format("   Is this a generic type definition? {0}\r\n",
        t.IsGenericTypeDefinition);
    
  3. Get an array that contains the generic type arguments, using the GetGenericArguments method.

    Dim typeParameters() As Type = t.GetGenericArguments()
    
    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.

    output.Text &= String.Format("   List {0} type arguments:" & vbCrLf, _
        typeParameters.Length)
    For Each tParam As Type In typeParameters
        If tParam.IsGenericParameter Then
            DisplayGenericParameter(tParam)
        Else
            output.Text &= String.Format("      Type argument: {0}" & vbCrLf, _
                tParam)
        End If
    Next
    
    output.Text += String.Format("   List {0} type arguments:\r\n", 
        typeParameters.Length);
    foreach( Type tParam in typeParameters )
    {
        if (tParam.IsGenericParameter)
        {
            DisplayGenericParameter(tParam);
        }
        else
        {
            output.Text += String.Format("      Type argument: {0}\r\n",
                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.

    Private Shared Sub DisplayGenericParameter(ByVal tp As Type)
        output.Text &= String.Format("      Type parameter: {0} position {1}" & vbCrLf, _
            tp.Name, tp.GenericParameterPosition)
    
    private static void DisplayGenericParameter(Type tp)
    {
        output.Text += String.Format("      Type parameter: {0} position {1}\r\n", 
            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.

    Dim classConstraint As Type = Nothing
    
    For Each iConstraint As Type In tp.GetGenericParameterConstraints()
        If iConstraint.IsInterface Then
            output.Text &= String.Format("         Interface constraint: {0}" & vbCrLf, _
                iConstraint)
        End If
    Next
    
    If classConstraint IsNot Nothing Then
        output.Text &= String.Format("         Base type constraint: {0}" & vbCrLf, _
            tp.BaseType)
    Else
        output.Text &= "         Base type constraint: None" & vbCrLf
    End If
    
    Type classConstraint = null;
    
    foreach(Type iConstraint in tp.GetGenericParameterConstraints())
    {
        if (iConstraint.IsInterface)
        {
            output.Text += String.Format("         Interface constraint: {0}\r\n",
                iConstraint);
        }
    }
    
    if (classConstraint != null)
    {
        output.Text += String.Format("         Base type constraint: {0}\r\n", 
            tp.BaseType);
    }
    else
        output.Text += "         Base type constraint: None\r\n"; 
    
  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.

    Dim sConstraints As GenericParameterAttributes = _
        tp.GenericParameterAttributes And _
        GenericParameterAttributes.SpecialConstraintMask
    
    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 Then
        output.Text &= "         No special constraints." & vbCrLf
    Else
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.DefaultConstructorConstraint) Then
            output.Text &= "         Must have a parameterless constructor." & vbCrLf
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.ReferenceTypeConstraint) Then
            output.Text &= "         Must be a reference type." & vbCrLf
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.NotNullableValueTypeConstraint) Then
            output.Text &= "         Must be a non-nullable value type." & vbCrLf
        End If
    End If
    
    if (sConstraints == GenericParameterAttributes.None)
    {
        output.Text += "         No special constraints.\r\n";
    }
    else
    {
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.DefaultConstructorConstraint))
        {
            output.Text += "         Must have a parameterless constructor.\r\n";
        }
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.ReferenceTypeConstraint))
        {
            output.Text += "         Must be a reference type.\r\n";
        }
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.NotNullableValueTypeConstraint))
        {
            output.Text += "         Must be a non-nullable value type.\r\n";
        }
    }
    

Constructing an Instance of a Generic 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 GetType operator to create the generic type 
    ' definition directly. To specify the generic type definition,
    ' omit the type arguments but retain the comma that separates
    ' them.
    Dim d1 As Type = GetType(Dictionary(Of ,))
    
    ' 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.
    Dim d2 As New Dictionary(Of 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.
    Dim d3 As Type = d2.GetType()
    Dim d4 As Type = d3.GetGenericTypeDefinition()
    
    // Use the typeof operator to create the generic type 
    // definition directly. To specify the generic type definition,
    // omit the type arguments but retain the comma that separates
    // them.
    Type d1 = typeof(Dictionary<,>);
    
    // 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 = new 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.

    Dim typeArgs() As Type = _
        { GetType(String), GetType(Example) }
    
    Type[] typeArgs = {typeof(string), typeof(Example)};
    
  3. Call the MakeGenericType method to bind the type arguments to the type parameters and construct the type.

    Dim constructed As Type = _
        d1.MakeGenericType(typeArgs)
    
    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.

    Dim o As Object = Activator.CreateInstance(constructed)
    
    object o = Activator.CreateInstance(constructed);
    

Example

The following code example defines a DisplayGenericType method to examine the generic type definitions and constructed types that are used in the code and to 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 that was constructed using MakeGenericType to a Type object that was 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.

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

' Define an example interface.
Public Interface ITestArgument
End Interface

' Define an example base class.
Public Class TestBase
End Class

' 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.
Public Class Test(Of T As {TestBase, ITestArgument, New})
End Class

' Define a class that meets the constraints on the type
' parameter of class Test.
Public Class TestArgument
    Inherits TestBase
    Implements ITestArgument
    Public Sub New()
    End Sub
End Class

Public Class Example

    ' Makes the TextBlock available to all methods in Example.
    Private Shared output As System.Windows.Controls.TextBlock

    ' The following method displays information about a generic
    ' type.
    Private Shared Sub DisplayGenericType(ByVal t As Type)
        output.Text &= vbCrLf & t.ToString() & vbCrLf
        output.Text &= "   Is this a generic type? " _ 
            & t.IsGenericType & vbCrLf
        output.Text &= "   Is this a generic type definition? " _ 
            & t.IsGenericTypeDefinition & vbCrLf

        ' Get the generic type parameters or type arguments.
        Dim typeParameters() As Type = t.GetGenericArguments()

        output.Text &= String.Format("   List {0} type arguments:" & vbCrLf, _
            typeParameters.Length)
        For Each tParam As Type In typeParameters
            If tParam.IsGenericParameter Then
                DisplayGenericParameter(tParam)
            Else
                output.Text &= String.Format("      Type argument: {0}" & vbCrLf, _
                    tParam)
            End If
        Next
    End Sub

    ' The following method displays information about a generic
    ' type parameter. Generic type parameters are represented by
    ' instances of System.Type, just like ordinary types.
    Private Shared Sub DisplayGenericParameter(ByVal tp As Type)
        output.Text &= String.Format("      Type parameter: {0} position {1}" & vbCrLf, _
            tp.Name, tp.GenericParameterPosition)

        Dim classConstraint As Type = Nothing

        For Each iConstraint As Type In tp.GetGenericParameterConstraints()
            If iConstraint.IsInterface Then
                output.Text &= String.Format("         Interface constraint: {0}" & vbCrLf, _
                    iConstraint)
            End If
        Next

        If classConstraint IsNot Nothing Then
            output.Text &= String.Format("         Base type constraint: {0}" & vbCrLf, _
                tp.BaseType)
        Else
            output.Text &= "         Base type constraint: None" & vbCrLf
        End If

        Dim sConstraints As GenericParameterAttributes = _
            tp.GenericParameterAttributes And _
            GenericParameterAttributes.SpecialConstraintMask
        If sConstraints = GenericParameterAttributes.None Then
            output.Text &= "         No special constraints." & vbCrLf
        Else
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.DefaultConstructorConstraint) Then
                output.Text &= "         Must have a parameterless constructor." & vbCrLf
            End If
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.ReferenceTypeConstraint) Then
                output.Text &= "         Must be a reference type." & vbCrLf
            End If
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.NotNullableValueTypeConstraint) Then
                output.Text &= "         Must be a non-nullable value type." & vbCrLf
            End If
        End If
    End Sub

    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

        ' Makes the TextBlock available to all methods in Example.
        output = outputBlock

        ' Two ways to get a Type object that represents the generic
        ' type definition of the Dictionary class. 
        '
        ' Use the GetType operator to create the generic type 
        ' definition directly. To specify the generic type definition,
        ' omit the type arguments but retain the comma that separates
        ' them.
        Dim d1 As Type = GetType(Dictionary(Of ,))

        ' 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.
        Dim d2 As New Dictionary(Of 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.
        Dim d3 As Type = d2.GetType()
        Dim d4 As Type = d3.GetGenericTypeDefinition()

        ' Display information for the generic type definition, and
        ' for the constructed type Dictionary(Of 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.
        Dim typeArgs() As Type = _
            { GetType(String), GetType(Example) }

        ' Construct the type Dictionary(Of String, Example).
        Dim constructed As Type = _
            d1.MakeGenericType(typeArgs)

        DisplayGenericType(constructed)

        Dim o As Object = Activator.CreateInstance(constructed)

        output.Text &= vbCrLf & _
            "Compare types obtained by different methods:" & vbCrLf
        output.Text &= "   Are the constructed types equal? " _
            & (d2.GetType() Is constructed) & vbCrLf
        output.Text &= "   Are the generic definitions equal? " _ 
            & (d1 Is constructed.GetGenericTypeDefinition()) & vbCrLf

        ' Demonstrate the DisplayGenericType and 
        ' DisplayGenericParameter methods with the Test class 
        ' defined above. This shows base, interface, and special
        ' constraints.
        DisplayGenericType(GetType(Test(Of )))
    End Sub
End Class

' This example produces output similar to the following:
'
'System.Collections.Generic.Dictionary`2[TKey,TValue]
'   Is this a generic type? True
'   Is this a generic type definition? True
'   List 2 type arguments:
'      Type parameter: TKey position 0
'         Base type constraint: None
'         No special constraints.
'      Type parameter: TValue position 1
'         Base type constraint: None
'         No special constraints.
'
'System.Collections.Generic.Dictionary`2[System.String,SilverlightApplication.Example]
'   Is this a generic type? True
'   Is this a generic type definition? False
'   List 2 type arguments:
'      Type argument: System.String
'      Type argument: SilverlightApplication.Example
'
'System.Collections.Generic.Dictionary`2[System.String,SilverlightApplication.Example]
'   Is this a generic type? True
'   Is this a generic type definition? False
'   List 2 type arguments:
'      Type argument: System.String
'      Type argument: SilverlightApplication.Example
'
'Compare types obtained by different methods:
'   Are the constructed types equal? True
'   Are the generic definitions equal? True
'
'Test`1[T]
'   Is this a generic type? True
'   Is this a generic type definition? True
'   List 1 type arguments:
'      Type parameter: T position 0
'         Interface constraint: SilverlightApplication.ITestArgument
'         Base type constraint: None
'         Must have a parameterless constructor.
using System;
using System.Reflection;
using System.Collections.Generic;

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

// Define an example base class.
public 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.
public class Test<T> where T : TestBase, ITestArgument, new() {}

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

public class Example
{
    // Makes the TextBlock available to all methods in Example.
    private static System.Windows.Controls.TextBlock output;

    // The following method displays information about a generic
    // type.
    private static void DisplayGenericType(Type t)
    {
        output.Text += String.Format("\r\n{0}\r\n", t);
        output.Text += String.Format("   Is this a generic type? {0}\r\n",
            t.IsGenericType);
        output.Text += String.Format("   Is this a generic type definition? {0}\r\n",
            t.IsGenericTypeDefinition);

        // Get the generic type parameters or type arguments.
        Type[] typeParameters = t.GetGenericArguments();

        output.Text += String.Format("   List {0} type arguments:\r\n", 
            typeParameters.Length);
        foreach( Type tParam in typeParameters )
        {
            if (tParam.IsGenericParameter)
            {
                DisplayGenericParameter(tParam);
            }
            else
            {
                output.Text += String.Format("      Type argument: {0}\r\n",
                    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.
    private static void DisplayGenericParameter(Type tp)
    {
        output.Text += String.Format("      Type parameter: {0} position {1}\r\n", 
            tp.Name, tp.GenericParameterPosition);

        Type classConstraint = null;

        foreach(Type iConstraint in tp.GetGenericParameterConstraints())
        {
            if (iConstraint.IsInterface)
            {
                output.Text += String.Format("         Interface constraint: {0}\r\n",
                    iConstraint);
            }
        }

        if (classConstraint != null)
        {
            output.Text += String.Format("         Base type constraint: {0}\r\n", 
                tp.BaseType);
        }
        else
            output.Text += "         Base type constraint: None\r\n"; 

        GenericParameterAttributes sConstraints = 
            tp.GenericParameterAttributes & 
            GenericParameterAttributes.SpecialConstraintMask;

        if (sConstraints == GenericParameterAttributes.None)
        {
            output.Text += "         No special constraints.\r\n";
        }
        else
        {
            if (GenericParameterAttributes.None != (sConstraints &
                GenericParameterAttributes.DefaultConstructorConstraint))
            {
                output.Text += "         Must have a parameterless constructor.\r\n";
            }
            if (GenericParameterAttributes.None != (sConstraints &
                GenericParameterAttributes.ReferenceTypeConstraint))
            {
                output.Text += "         Must be a reference type.\r\n";
            }
            if (GenericParameterAttributes.None != (sConstraints &
                GenericParameterAttributes.NotNullableValueTypeConstraint))
            {
                output.Text += "         Must be a non-nullable value type.\r\n";
            }
        }
    }

    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        // Makes the TextBlock available to all methods in Example.
        output = outputBlock; 

        // Two ways to get a Type object that represents the generic
        // type definition of the Dictionary class. 
        //
        // Use the typeof operator to create the generic type 
        // definition directly. To specify the generic type definition,
        // omit the type arguments but retain the comma that separates
        // them.
        Type d1 = typeof(Dictionary<,>);

        // 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 = new 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.
        Type[] typeArgs = {typeof(string), typeof(Example)};

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

        DisplayGenericType(constructed);

        object o = Activator.CreateInstance(constructed);

        output.Text += "\r\nCompare types obtained by different methods:\r\n";
        output.Text += String.Format("   Are the constructed types equal? {0}\r\n",
            (d2.GetType()==constructed));
        output.Text += String.Format("   Are the generic definitions equal? {0}\r\n",
            (d1==constructed.GetGenericTypeDefinition()));

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

/* This example produces the following output:

System.Collections.Generic.Dictionary`2[TKey,TValue]
   Is this a generic type? True
   Is this a generic type definition? True
   List 2 type arguments:
      Type parameter: TKey position 0
         Base type constraint: None
         No special constraints.
      Type parameter: TValue position 1
         Base type constraint: None
         No special constraints.

System.Collections.Generic.Dictionary`2[System.String,Example]
   Is this a generic type? True
   Is this a generic type definition? False
   List 2 type arguments:
      Type argument: System.String
      Type argument: Example

System.Collections.Generic.Dictionary`2[System.String,Example]
   Is this a generic type? True
   Is this a generic type definition? False
   List 2 type arguments:
      Type argument: System.String
      Type argument: Example

Compare types obtained by different methods:
   Are the constructed types equal? True
   Are the generic definitions equal? True

Test`1[T]
   Is this a generic type? True
   Is this a generic type definition? True
   List 1 type arguments:
      Type parameter: T position 0
         Interface constraint: ITestArgument
         Base type constraint: None
         Must have a parameterless constructor.
 */

Compiling the Code

  • The code contains the C# using statements (Imports in Visual Basic) that are 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 example into one source file.