Exporter (0) Imprimer
Développer tout

Comment : examiner et instancier des types génériques avec la réflexion

Les informations sur des types génériques sont obtenues de la même façon que les informations sur les autres types : par l'examen d'un objet Type qui représente le type générique. La principale différence réside dans le fait qu'un type générique possède une liste d'objets Type qui représentent ses paramètres de type générique. La première procédure de cette section examine des types génériques.

Vous pouvez créer un objet Type qui représente un type construit en liant des arguments de type aux paramètres de type d'une définition de type générique. Cette action est illustrée dans la deuxième procédure.

Pour examiner un type générique et ses paramètres de type

  1. Obtenez une instance de Type qui représente le type générique. Dans le code suivant, le type est obtenu à l'aide de l'opérateur typeof C# (GetType en Visual Basic, typeid en Visual C++). Consultez la rubrique de la classe Type pour connaître d'autres moyens d'obtenir un objet Type. Notez que dans le reste de cette procédure, le type est contenu dans un paramètre de méthode nommé t.

    Type d1 = typeof(Dictionary<,>);
    
    
  2. Utilisez la propriété IsGenericType pour déterminer si le type est générique et utilisez la propriété IsGenericTypeDefinition pour déterminer si le type est une définition de type générique.

    Console.WriteLine("   Is this a generic type? {0}",
        t.IsGenericType);
    Console.WriteLine("   Is this a generic type definition? {0}",
        t.IsGenericTypeDefinition);
    
    
  3. Obtenez un tableau qui contient les arguments de type générique, à l'aide de la méthode GetGenericArguments.

    Type[] typeParameters = t.GetGenericArguments();
    
    
  4. Pour chaque argument de type, déterminez s'il s'agit d'un paramètre de type (par exemple, dans une définition de type générique) ou d'un type spécifié pour un paramètre de type (par exemple, dans un type construit), à l'aide de la propriété IsGenericParameter.

    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. Dans le système de types, un paramètre de type générique est représenté par une instance de Type, de la même façon que les types ordinaires. Le code suivant affiche le nom et la position de paramètre d'un objet Type qui représente un paramètre de type générique. La position de paramètre est ici sans importance ; elle est plus utile lorsque vous examinez un paramètre de type qui a été utilisé comme argument de type d'un autre type générique.

    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine("      Type parameter: {0} position {1}", 
            tp.Name, tp.GenericParameterPosition);
    
    
  6. Déterminez la contrainte du type de base et les contraintes d'interface d'un paramètre de type générique en utilisant la méthode GetGenericParameterConstraints pour obtenir toutes les contraintes dans un seul tableau. Rien ne garantit que les contraintes respectent un ordre particulier.

    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. Utilisez la propriété GenericParameterAttributes pour découvrir les contraintes spéciales d'un paramètre de type, par exemple l'obligation d'être un type référence. La propriété inclut également des valeurs qui représentent la variance, que vous pouvez masquer comme l'illustre le code suivant.

    GenericParameterAttributes sConstraints = 
        tp.GenericParameterAttributes & 
        GenericParameterAttributes.SpecialConstraintMask;
    
    
  8. Les attributs de contrainte spéciaux sont des indicateurs, et le même indicateur (System.Reflection.GenericParameterAttributes.None) qui ne représente aucune contrainte spéciale ne représente non plus aucune covariance ou contravariance. Par conséquent, pour tester l'un ou l'autre de ces conditions vous devez utiliser le masque approprié. Dans ce cas, utilisez System.Reflection.GenericParameterAttributes.SpecialConstraintMask pour isoler les indicateurs de contrainte spéciaux.

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

Construction d'une instance d'un type générique

Un type générique est similaire à un modèle. Vous ne pouvez pas créer d'instances du type générique à moins de spécifier des types réels en tant que paramètres de type générique de celui-ci. Pour le faire au moment de l'exécution, à l'aide de la réflexion, vous avez besoin de la méthode MakeGenericType.

Pour construire une instance d'un type générique

  1. Obtenez un objet Type qui représente le type spécifié. Le code suivant obtient le type Dictionary générique de deux façons différentes : en utilisant la méthode surchargée System.Type.GetType(System.String) avec une chaîne qui décrit le type, et en appelant la méthode GetGenericTypeDefinition sur le type construit Dictionary<String, Example> (Dictionary(Of String, Example) en Visual Basic). La méthode MakeGenericType exige une définition de type générique.

    // 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. Construisez un tableau des arguments de type pour substituer les paramètres de type. Le tableau doit contenir le nombre correct d'objets Type, et respecter l'ordre de la liste des paramètres de type. Dans ce cas, la clé (premier paramètre de type) est de type String, et les valeurs du dictionnaire sont des instances d'une classe nommée Example.

    Type[] typeArgs = {typeof(string), typeof(Example)};
    
    
  3. Appelez la méthode MakeGenericType pour lier les arguments de type aux paramètres de type et construire le type.

    Type constructed = d1.MakeGenericType(typeArgs);
    
    
  4. Utilisez la méthode surchargée CreateInstance pour créer un objet du type construit. Le code suivant stocke deux instances de la classe Example dans l'objet Dictionary<String, Example> résultant.

    object o = Activator.CreateInstance(constructed);
    
    

Exemple

L'exemple de code suivant définit une méthode DisplayGenericType pour examiner les définitions de types génériques et les types construits utilisés dans le code et afficher leurs informations. La méthode DisplayGenericType montre comment utiliser les propriétés IsGenericType, IsGenericParameter et GenericParameterPosition et la méthode GetGenericArguments.

L'exemple définit également une méthode DisplayGenericParameter pour examiner un paramètre de type générique et afficher ses contraintes.

L'exemple de code définit un ensemble de types de test, y compris un type générique qui illustre des contraintes de paramètre de type, et montre comment afficher les informations sur ces types.

L'exemple construit un type de la classe Dictionary en créant un tableau des arguments de type et appelant la méthode MakeGenericType. Le programme compare l'objet Type construit à l'aide de la méthode MakeGenericType à un objet Type obtenu à l'aide de typeof (GetType en Visual Basic), démontrant qu'ils sont identiques. De la même façon, le programme utilise la méthode GetGenericTypeDefinition pour obtenir la définition de type générique du type construit et la compare à l'objet Type qui représente la classe Dictionary.

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

Compilation du code

  • Le code contient les instructions using C# (Imports en Visual Basic) nécessaires à la compilation.

  • Aucune référence d'assembly supplémentaire n'est requise.

  • Compilez le code à la ligne de commande à l'aide de csc.exe, vbc.exe ou cl.exe. Pour compiler le code dans Visual Studio, placez-le dans un modèle de projet d'application console.

Voir aussi

Ajouts de la communauté

AJOUTER
Microsoft réalise une enquête en ligne pour recueillir votre opinion sur le site Web de MSDN. Si vous choisissez d’y participer, cette enquête en ligne vous sera présentée lorsque vous quitterez le site Web de MSDN.

Si vous souhaitez y participer,
Afficher:
© 2014 Microsoft