Gewusst wie: Untersuchen und Instanziieren von generischen Typen mit Reflektion

Aktualisiert: November 2007

Die Informationen über generische Typen werden, genau wie Informationen über andere Typen, durch Untersuchen eines Type-Objekts abgerufen, dass den generischen Typ darstellt. Der grundsätzliche Unterschied liegt darin, dass ein generischer Typ über eine Liste von Type-Objekten verfügt, die seine generischen Typparameter darstellen. Die erste Prozedur in diesem Abschnitt untersucht generische Typen.

Sie können ein Type-Objekt erstellen, das einen konstruierten Typ darstellt, indem Sie Typargumente an die Typparameter einer Definition eines generischen Typs binden. Dies wird in der zweiten Prozedur veranschaulicht.

So untersuchen Sie einen generischen Typ und seine Typparameter

  1. Rufen Sie eine Instanz von Type ab, die den generischen Typ darstellt. Im folgenden Code wird der Typ mit dem typeof-Operator in C# (GetType in Visual Basic, typeid in Visual C++) abgerufen. Andere Möglichkeiten zum Abrufen eines Type-Objekts finden Sie im Thema über die Type-Klasse. Beachten Sie, dass sich der Typ im Rest der Prozedur in einem Methodenparameter mit der Bezeichnung t befindet.

    Dim d1 As Type = GetType(Dictionary(Of ,))
    
    Type d1 = typeof(Dictionary<,>);
    
  2. Ermitteln Sie über die IsGenericType-Eigenschaft, ob es sich um einen generischen Typ handelt, und über die IsGenericTypeDefinition-Eigenschaft, ob es sich bei dem Typ um eine Definition eines generischen Typs handelt.

    Console.WriteLine("   Is this a generic type? " _ 
        & t.IsGenericType)
    Console.WriteLine("   Is this a generic type definition? " _ 
        & t.IsGenericTypeDefinition)
    
    Console.WriteLine("   Is this a generic type? {0}",
        t.IsGenericType);
    Console.WriteLine("   Is this a generic type definition? {0}",
        t.IsGenericTypeDefinition);
    
  3. Rufen Sie mithilfe der GetGenericArguments-Methode ein Array ab, das die generischen Typargumente enthält.

    Dim typeParameters() As Type = t.GetGenericArguments()
    
    Type[] typeParameters = t.GetGenericArguments();
    
  4. Bestimmen Sie anhand der IsGenericParameter-Eigenschaft für jedes Typargument, ob es sich um einen Typparameter (z. B. in einer Definition eines generischen Typs) oder um einen Typ handelt, der für einen Typparameter angegeben wurde (z. B. in einem konstruierten Typ).

    Console.WriteLine("   List {0} type arguments:", _
        typeParameters.Length)
    For Each tParam As Type In typeParameters
        If tParam.IsGenericParameter Then
            DisplayGenericParameter(tParam)
        Else
            Console.WriteLine("      Type argument: {0}", _
                tParam)
        End If
    Next
    
    Console.WriteLine("   List {0} type arguments:", 
        typeParameters.Length);
    foreach( Type tParam in typeParameters )
    {
        if (tParam.IsGenericParameter)
        {
            DisplayGenericParameter(tParam);
        }
        else
        {
            Console.WriteLine("      Type argument: {0}",
                tParam);
        }
    }
    
  5. Ein generischer Typparameter wird innerhalb des Typsystems, genau wie ein normaler Typ, durch eine Instanz von Type dargestellt. Im folgenden Code werden der Name und die Parameterposition eines Type-Objekts angezeigt, das einen generischen Typparameter darstellt. Die Parameterposition hat in diesem Fall keine Bedeutung. Sie kann aber z. B. von Interesse sein, wenn Sie einen Typparameter untersuchen, der von einem anderen generischen Typ als Typargument verwendet wurde.

    Private Shared Sub DisplayGenericParameter(ByVal tp As Type)
        Console.WriteLine("      Type parameter: {0} position {1}", _
            tp.Name, tp.GenericParameterPosition)
    
    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine("      Type parameter: {0} position {1}", 
            tp.Name, tp.GenericParameterPosition);
    
  6. Ermitteln Sie die Basistypeinschränkung und die Schnittstelleneinschränkungen eines generischen Typparameters mithilfe der GetGenericParameterConstraints-Methode, um sämtliche Einschränkungen in einem einzelnen Array abzurufen. Die Einschränkungen werden nicht unbedingt in einer bestimmten Reihenfolge angezeigt.

    Dim classConstraint As Type = Nothing
    
    For Each iConstraint As Type In tp.GetGenericParameterConstraints()
        If iConstraint.IsInterface Then
            Console.WriteLine("         Interface constraint: {0}", _
                iConstraint)
        End If
    Next
    
    If classConstraint IsNot Nothing Then
        Console.WriteLine("         Base type constraint: {0}", _
            tp.BaseType)
    Else
        Console.WriteLine("         Base type constraint: None")
    End If
    
    Type classConstraint = null;
    
    foreach(Type iConstraint in tp.GetGenericParameterConstraints())
    {
        if (iConstraint.IsInterface)
        {
            Console.WriteLine("         Interface constraint: {0}",
                iConstraint);
        }
    }
    
    if (classConstraint != null)
    {
        Console.WriteLine("         Base type constraint: {0}", 
            tp.BaseType);
    }
    else
        Console.WriteLine("         Base type constraint: None"); 
    
  7. Mit der GenericParameterAttributes-Eigenschaft können Sie die besonderen Einschränkungen für einen Typparameter erkennen, z. B., dass dieser ein Verweistyp sein muss. Die Eigenschaft enthält auch Werte, die Varianz darstellen, die Sie, wie im folgenden Code dargestellt, maskieren können.

    Dim sConstraints As GenericParameterAttributes = _
        tp.GenericParameterAttributes And _
        GenericParameterAttributes.SpecialConstraintMask
    
    GenericParameterAttributes sConstraints = 
        tp.GenericParameterAttributes & 
        GenericParameterAttributes.SpecialConstraintMask;
    
  8. Die Attribute für besondere Einschränkungen sind Flags, und das Flag (GenericParameterAttributes.None), das bedeutet, dass keine besonderen Einschränkungen vorliegen, bedeutet auch, dass keine Kovarianz oder Kontravarianz vorhanden ist. Um zu überprüfen, ob eine dieser Bedingungen zutrifft, müssen Sie daher die entsprechende Maske verwenden. Verwenden Sie in diesem Fall GenericParameterAttributes.SpecialConstraintMask, um die Flags für besondere Einschränkungen zu isolieren.

    If sConstraints = GenericParameterAttributes.None Then
        Console.WriteLine("         No special constraints.")
    Else
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.DefaultConstructorConstraint) Then
            Console.WriteLine("         Must have a parameterless constructor.")
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.ReferenceTypeConstraint) Then
            Console.WriteLine("         Must be a reference type.")
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.NotNullableValueTypeConstraint) Then
            Console.WriteLine("         Must be a non-nullable value type.")
        End If
    End If
    
    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.");
        }
    }
    

Erstellen einer Instanz eines generischen Typs

Ein generischer Typ ist im Prinzip eine Vorlage. Sie können keine Instanzen aus diesem erstellen, es sei denn, Sie geben für seine generischen Typparameter echte Typen an. Um dieses zur Laufzeit mit Reflektion durchzuführen, müssen Sie die MakeGenericType-Methode verwenden.

So erstellen Sie eine Instanz eines generischen Typs

  1. Rufen Sie ein Type-Objekt ab, das den generischen Typ darstellt. Im folgenden Code wird der generische Typ Dictionary<TKey, TValue> auf zwei Arten abgerufen: anhand der Type.GetType(String)-Methodenüberladung mit einer Zeichenfolge, die den Typ beschreibt, und durch Aufrufen der GetGenericTypeDefinition-Methode für den konstruierten Typ Dictionary<String, Example> (Dictionary(Of String, Example) in Visual Basic). Für die MakeGenericType-Methode ist eine Definition eines generischen Typs erforderlich.

    ' 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. Erstellen Sie ein Array von Typargumenten, die die Typparameter ersetzen sollen. Das Array muss die richtige Anzahl von Type-Objekten in der Reihenfolge enthalten, in der sie in der Typparameterliste aufgeführt sind. In diesem Fall ist der Schlüssel (der erste Typparameter) vom Typ String, und die Werte im Wörterbuch sind Instanzen einer Klasse mit der Bezeichnung Example.

    Dim typeArgs() As Type = _
        { GetType(String), GetType(Example) }
    
    Type[] typeArgs = {typeof(string), typeof(Example)};
    
  3. Rufen Sie die MakeGenericType-Methode auf, um die Typargumente an die Typparameter zu binden und den Typ zu erstellen.

    Dim constructed As Type = _
        d1.MakeGenericType(typeArgs)
    
    Type constructed = d1.MakeGenericType(typeArgs);
    
  4. Erstellen Sie mithilfe der CreateInstance(Type)-Methodenüberladung ein Objekt des konstruierten Typs. Im folgenden Code werden zwei Instanzen der Example-Klasse im resultierenden Dictionary<String, Example>-Objekt gespeichert.

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

Beispiel

Im folgenden Codebeispiel wird eine DisplayGenericType-Methode definiert, mit der die im Code verwendeten Definitionen des generischen Typs und konstruierte Typen untersucht und die entsprechenden Informationen angezeigt werden. Die DisplayGenericType-Methode zeigt, wie die Eigenschaften IsGenericType, IsGenericParameter und GenericParameterPosition sowie die GetGenericArguments-Methode verwendet werden.

Weiterhin wird im Beispiel eine DisplayGenericParameter-Methode definiert, mit der ein generischer Typparameter untersucht und seine Einschränkungen angezeigt werden sollen.

Im Codebeispiel wird eine Gruppe von Testtypen definiert, u. a. ein generischer Typ, der die Einschränkungen der Typparameter veranschaulicht und zeigt, wie Informationen über diese Typen angezeigt werden können.

Es wird ein Typ aus der Dictionary<TKey, TValue>-Klasse konstruiert, indem ein Array von Typparametern erstellt und die MakeGenericType-Methode aufgerufen wird. Das Programm vergleicht das mit MakeGenericType erstellte Type-Objekt mit einem Type-Objekt, das über typeof (GetType in Visual Basic) abgerufen wurde, um zu verdeutlichen, dass sie gleich sind. Entsprechend muss das Programm die Definition des generischen Typs für den konstruierten Typ mithilfe der GetGenericTypeDefinition-Methode abrufen und diese mit dem Type-Objekt vergleichen, das die Dictionary<TKey, TValue>-Klasse darstellt.

Imports System
Imports System.Reflection
Imports System.Collections.Generic
Imports System.Security.Permissions

' 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
    ' The following method displays information about a generic
    ' type.
    Private Shared Sub DisplayGenericType(ByVal t As Type)
        Console.WriteLine(vbCrLf & t.ToString())
        Console.WriteLine("   Is this a generic type? " _ 
            & t.IsGenericType)
        Console.WriteLine("   Is this a generic type definition? " _ 
            & t.IsGenericTypeDefinition)

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

        Console.WriteLine("   List {0} type arguments:", _
            typeParameters.Length)
        For Each tParam As Type In typeParameters
            If tParam.IsGenericParameter Then
                DisplayGenericParameter(tParam)
            Else
                Console.WriteLine("      Type argument: {0}", _
                    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)
        Console.WriteLine("      Type parameter: {0} position {1}", _
            tp.Name, tp.GenericParameterPosition)

        Dim classConstraint As Type = Nothing

        For Each iConstraint As Type In tp.GetGenericParameterConstraints()
            If iConstraint.IsInterface Then
                Console.WriteLine("         Interface constraint: {0}", _
                    iConstraint)
            End If
        Next

        If classConstraint IsNot Nothing Then
            Console.WriteLine("         Base type constraint: {0}", _
                tp.BaseType)
        Else
            Console.WriteLine("         Base type constraint: None")
        End If

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

    <PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
    Public Shared Sub Main()
        ' 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)

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

        ' 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
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Security.Permissions;

// 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
{
    // 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.
        Type[] typeParameters = t.GetGenericArguments();

        Console.WriteLine("   List {0} type arguments:", 
            typeParameters.Length);
        foreach( 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.
    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine("      Type parameter: {0} position {1}", 
            tp.Name, tp.GenericParameterPosition);

        Type classConstraint = null;

        foreach(Type iConstraint in tp.GetGenericParameterConstraints())
        {
            if (iConstraint.IsInterface)
            {
                Console.WriteLine("         Interface constraint: {0}",
                    iConstraint);
            }
        }

        if (classConstraint != null)
        {
            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.");
            }
        }
    }

    [PermissionSetAttribute(SecurityAction.Demand, Name="FullTrust")]
    public static void Main()
    {
        // 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);

        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(typeof(Test<>));
    }
}

Kompilieren des Codes

  • Der Code enthält die für die Kompilierung erforderlichen using-Anweisungen für C# (Imports in Visual Basic).

  • Er werden keine weiteren Assemblyverweise benötigt.

  • Kompilieren Sie den Code mithilfe von csc.exe, vbc.exe oder cl.exe über die Befehlszeile. Um den Code in Visual Studio zu kompilieren, fügen Sie ihn in eine Projektvorlage für eine Konsolenanwendung ein.

Siehe auch

Konzepte

Anzeigen von Typinformationen

Übersicht über Generika in .NET Framework

Referenz

Type

MethodInfo

Weitere Ressourcen

Reflektion und generische Typen