Condividi tramite


Procedura: Esaminare e creare istanze di tipi generici con riflessione

Le informazioni sui tipi generici vengono ottenute nello stesso modo delle informazioni su altri tipi: esaminando un Type oggetto che rappresenta il tipo generico. La differenza principale è che un tipo generico ha un elenco di Type oggetti che rappresentano i parametri di tipo generico. La prima procedura di questa sezione esamina i tipi generici.

È possibile creare un oggetto che rappresenta un Type tipo costruito associando argomenti di tipo ai parametri di tipo di una definizione di tipo generico. La seconda procedura illustra questa operazione.

Per esaminare un tipo generico e i relativi parametri di tipo

  1. Ottieni un'istanza di Type che rappresenta il tipo generico. Nel codice seguente il tipo viene ottenuto usando l'operatore C# typeof (GetType in Visual Basic). Per altri modi per ottenere un Type oggetto, vedere Type. Nella parte restante di questa procedura, il tipo è contenuto in un parametro del metodo denominato t.

    Type d1 = typeof(Dictionary<,>);
    
    Dim d1 As Type = GetType(Dictionary(Of ,))
    
  2. Utilizzare la IsGenericType proprietà per determinare se il tipo è generico e utilizzare la IsGenericTypeDefinition proprietà per determinare se il tipo è una definizione di tipo generico.

    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? " _
        & t.IsGenericType)
    Console.WriteLine("   Is this a generic type definition? " _
        & t.IsGenericTypeDefinition)
    
  3. Ottenere una matrice che contiene gli argomenti di tipo generico usando il GetGenericArguments metodo .

    Type[] typeParameters = t.GetGenericArguments();
    
    Dim typeParameters() As Type = t.GetGenericArguments()
    
  4. Per ogni argomento di tipo, determinare se si tratta di un parametro di tipo (ad esempio, in una definizione di tipo generico) o di un tipo specificato per un parametro di tipo (ad esempio, in un tipo costruito), usando la IsGenericParameter proprietà .

    Console.WriteLine($"   List {typeParameters.Length} type arguments:");
    foreach (Type tParam in typeParameters)
    {
        if (tParam.IsGenericParameter)
        {
            DisplayGenericParameter(tParam);
        }
        else
        {
            Console.WriteLine($"      Type argument: {tParam}");
        }
    }
    
    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
    
  5. Nel sistema dei tipi, un parametro di tipo generico è rappresentato da un'istanza di Type, esattamente come sono i tipi normali. Nel codice seguente vengono visualizzati il nome e la posizione del parametro di un Type oggetto che rappresenta un parametro di tipo generico. Qui, la posizione del parametro è un'informazione banale; diventa più interessante quando si esamina un parametro di tipo utilizzato come argomento di tipo per un altro tipo generico.

    Console.WriteLine($"      Type parameter: {tp.Name} position {tp.GenericParameterPosition}");
    
    Private Shared Sub DisplayGenericParameter(ByVal tp As Type)
        Console.WriteLine("      Type parameter: {0} position {1}", _
            tp.Name, tp.GenericParameterPosition)
    
  6. Determinare il vincolo del tipo di base e i vincoli di interfaccia di un parametro di tipo generico usando il GetGenericParameterConstraints metodo per ottenere tutti i vincoli in una singola matrice. Non è garantito che i vincoli siano in un ordine particolare.

    foreach (Type iConstraint in tp.GetGenericParameterConstraints())
    {
        if (iConstraint.IsInterface)
        {
            Console.WriteLine($"         Interface constraint: {iConstraint}");
        }
    }
    
    Console.WriteLine($"         Base type constraint: {tp.BaseType ?? tp.BaseType: None}");
    
    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
    
  7. Utilizzare la GenericParameterAttributes proprietà per individuare i vincoli speciali su un parametro di tipo, ad esempio richiedendo che sia un tipo riferimento. La proprietà include anche valori che rappresentano la varianza, che è possibile mascherare come illustrato nel codice seguente.

    GenericParameterAttributes sConstraints =
        tp.GenericParameterAttributes &
        GenericParameterAttributes.SpecialConstraintMask;
    
    Dim sConstraints As GenericParameterAttributes = _
        tp.GenericParameterAttributes And _
        GenericParameterAttributes.SpecialConstraintMask
    
  8. Gli attributi speciali del vincolo sono flag e lo stesso flag (GenericParameterAttributes.None) che rappresenta l'assenza di vincoli speciali rappresenta anche l'assenza di covarianza o controvarianza. Pertanto, per verificare una di queste condizioni, è necessario usare la maschera appropriata. In questo caso, usare GenericParameterAttributes.SpecialConstraintMask per isolare i flag di vincolo speciali.

    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.");
        }
    }
    
    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
    

Costruire un'istanza di un tipo generico

Un tipo generico è simile a un modello. Non è possibile crearne istanze a meno che non si specifichino tipi reali per i relativi parametri di tipo generico. Per fare questo in fase di esecuzione, tramite reflection, è richiesto il metodo MakeGenericType.

  1. Ottiene un Type oggetto che rappresenta il tipo generico. Il codice seguente ottiene il tipo Dictionary<TKey,TValue> generico in due modi diversi: usando l'overload del Type.GetType(String) metodo con una stringa che descrive il tipo e chiamando il GetGenericTypeDefinition metodo sul tipo Dictionary\<String, Example> costruito (Dictionary(Of String, Example) in Visual Basic). Il MakeGenericType metodo richiede una definizione di tipo generico.

    // 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 = [];
    // 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();
    
    ' 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()
    
  2. Costruire una matrice di argomenti di tipo da sostituire con i parametri di tipo. La matrice deve contenere il numero corretto di Type oggetti, nello stesso ordine in cui vengono visualizzati nell'elenco dei parametri di tipo. In questo caso, la chiave (primo parametro di tipo) è di tipo Stringe i valori nel dizionario sono istanze di una classe denominata Example.

    Type[] typeArgs = [typeof(string), typeof(Example)];
    
    Dim typeArgs() As Type = _
        {GetType(String), GetType(Example)}
    
  3. Chiamare il MakeGenericType metodo per associare gli argomenti di tipo ai parametri di tipo e costruire il tipo.

    Type constructed = d1.MakeGenericType(typeArgs);
    
    Dim constructed As Type = _
        d1.MakeGenericType(typeArgs)
    
  4. Utilizzare il metodo CreateInstance(Type) overload per creare un oggetto del tipo costruito. Il codice seguente archivia due istanze della Example classe nell'oggetto risultante Dictionary<String, Example> .

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

Esempio

Nell'esempio di codice seguente viene definito un DisplayGenericType metodo per esaminare le definizioni dei tipi generici e i tipi costruiti usati nel codice e visualizzare le relative informazioni. Il metodo DisplayGenericType illustra come usare le proprietà IsGenericType, IsGenericParameter e GenericParameterPosition e il metodo GetGenericArguments.

Nell'esempio viene inoltre definito un metodo per esaminare un DisplayGenericParameter parametro di tipo generico e visualizzarne i vincoli.

Nell'esempio di codice viene definito un set di tipi di test, incluso un tipo generico che illustra i vincoli dei parametri di tipo e viene illustrato come visualizzare informazioni su questi tipi.

Nell'esempio viene creato un tipo dalla Dictionary<TKey,TValue> classe creando una matrice di argomenti di tipo e chiamando il MakeGenericType metodo . Il programma confronta l'oggetto Type costruito utilizzando MakeGenericType un Type oggetto ottenuto usando typeof (GetType in Visual Basic), dimostrando che sono gli stessi. Analogamente, il programma usa il GetGenericTypeDefinition metodo per ottenere la definizione di tipo generico del tipo costruito e lo confronta con l'oggetto Type che rappresenta la Dictionary<TKey,TValue> classe .

using System.Reflection;

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

        Console.WriteLine($"   List {typeParameters.Length} type arguments:");
        foreach (Type tParam in typeParameters)
        {
            if (tParam.IsGenericParameter)
            {
                DisplayGenericParameter(tParam);
            }
            else
            {
                Console.WriteLine($"      Type argument: {tParam}");
            }
        }
    }

    // Displays information about a generic type parameter.
    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine($"      Type parameter: {tp.Name} position {tp.GenericParameterPosition}");

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

        Console.WriteLine($"         Base type constraint: {tp.BaseType ?? tp.BaseType: 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 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 = [];
        // 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);
        _ = Activator.CreateInstance(constructed);

        Console.WriteLine("\r\nCompare types obtained by different methods:");
        Console.WriteLine($"   Are the constructed types equal? {d2.GetType() == constructed}");
        Console.WriteLine($"   Are the generic definitions equal? {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<>));
    }
}
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
    ' 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

    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

Vedere anche