英語で読む

次の方法で共有


方法: リフレクションでジェネリック型をチェックしてインスタンスを作成する

ジェネリック型の情報は、他の型の情報と同じ方法で取得します。つまり、ジェネリック型を表す Type オブジェクトをチェックすることによって取得します。 根本的な違いとして、ジェネリック型には、そのジェネリック型パラメーターを表す Type オブジェクトの一覧が含まれています。 このセクションでは、まず、ジェネリック型をチェックする手順を示します。

ジェネリック型の定義の型パラメーターに型引数をバインドすることにより、構築された型を表す Type オブジェクトを作成できます。 2 番目の手順でこの方法を示します。

ジェネリック型とその型パラメーターを確認するには

  1. ジェネリック型を表す Type のインスタンスを取得します。 次のコードでは、C# の typeof 演算子 (Visual Basic では GetType、Visual C++ では typeid) を使用して型を取得します。 Type オブジェクトを取得する他の方法については、Type クラスのトピックを参照してください。 この手順では、これ以降、型は t という名前のメソッド パラメーターに格納されます。

    C#
    Type d1 = typeof(Dictionary<,>);
    
  2. IsGenericType プロパティを使用して、型がジェネリックかどうかを確認し、IsGenericTypeDefinition プロパティを使用して型がジェネリック型の定義かどうかを確認します。

    C#
    Console.WriteLine("   Is this a generic type? {0}",
        t.IsGenericType);
    Console.WriteLine("   Is this a generic type definition? {0}",
        t.IsGenericTypeDefinition);
    
  3. GetGenericArguments メソッドを使用して、ジェネリック型引数が格納された配列を取得します。

    C#
    Type[] typeParameters = t.GetGenericArguments();
    
  4. 各型引数について、IsGenericParameter プロパティを使用して、その型引数が型パラメーター (ジェネリック型定義の型パラメーターなど) であるか、型パラメーターに指定された型 (構築された型の型パラメーターなど) であるかを確認します。

    C#
    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. 型システムでは、ジェネリック型パラメーターは、通常の型と同様に Type のインスタンスによって表されます。 次のコードでは、ジェネリック型パラメーターを表す Type オブジェクトの名前とパラメーター位置を表示します。 パラメーター位置はここでは重要な情報ではありませんが、別のジェネリック型の型引数として使用されている型パラメーターをチェックする場合に重要となります。

    C#
    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine("      Type parameter: {0} position {1}",
            tp.Name, tp.GenericParameterPosition);
    
  6. GetGenericParameterConstraints メソッドを使用して、ジェネリック型パラメーターの基本型の制約とインターフェイスの制約を確認し、単一の配列内のすべての制約を取得します。 制約は、特定の順序であるという保証はありません。

    C#
    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. GenericParameterAttributes プロパティを使用して、型パラメーターの特殊な制約 (参照型であることが必要など) を確認します。 このプロパティには、分散を表す値も含まれています。次のコードに示すように、この値にはマスクを設定できます。

    C#
    GenericParameterAttributes sConstraints =
        tp.GenericParameterAttributes &
        GenericParameterAttributes.SpecialConstraintMask;
    
  8. 特殊な制約の属性はフラグであり、特殊な制約がないことを表すフラグ (GenericParameterAttributes.None) と同じフラグが、共変性や反変性がないことも表します。 したがって、これらの条件のいずれかを調べるには、適切なマスクを使用する必要があります。 ここでは、GenericParameterAttributes.SpecialConstraintMask を使用して、特殊な制約のフラグを切り分けます。

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

ジェネリック型のインスタンスをコンストラクトする

ジェネリック型はテンプレートに似ています。 ジェネリック型パラメーターの実際の型を指定しない限り、インスタンスを作成することはできません。 リフレクションを使用して実行時にこれを行うには、MakeGenericType メソッドが必要です。

  1. ジェネリック型を表す Type オブジェクトを取得します。 次のコードでは、2 とおりの方法でジェネリック型 Dictionary<TKey,TValue> を取得します。型を示す文字列を指定して Type.GetType(String) メソッド オーバーロードを使用する方法と、構築された型 Dictionary\<String, Example> (Visual Basic では Dictionary(Of String, Example)) で GetGenericTypeDefinition メソッドを呼び出す方法です。 MakeGenericType メソッドには、ジェネリック型の定義が必要です。

    C#
    // 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. 型引数の配列を構築して、型パラメーターと置き換えます。 配列には、型パラメーター リストに表示される順序と同じ順序で、正確な数の Type オブジェクトを格納する必要があります。 この場合、キー (最初の型パラメーター) の型は String であり、ディクショナリの値は Example というクラスのインスタンスです。

    C#
    Type[] typeArgs = {typeof(string), typeof(Example)};
    
  3. MakeGenericType メソッドを呼び出して型引数を型パラメーターにバインドし、型を構築します。

    C#
    Type constructed = d1.MakeGenericType(typeArgs);
    
  4. CreateInstance(Type) メソッド オーバーロードを使用して、構築された型のオブジェクトを作成します。 次のコードでは、作成された Dictionary<String, Example> オブジェクトに、Example クラスの 2 つのインスタンスを格納します。

    C#
    object o = Activator.CreateInstance(constructed);
    

DisplayGenericType メソッドを定義して、コードで使用されているジェネリック型の定義と構築された型をチェックし、この情報を表示するコード例を次に示します。 DisplayGenericType メソッドは、IsGenericTypeIsGenericParameterGenericParameterPosition の各プロパティと GetGenericArguments メソッドの使用方法を示します。

この例では、DisplayGenericParameter メソッドも定義してジェネリック型パラメーターをチェックし、その制約を表示します。

このコード例では、型パラメーターの制約を示すジェネリック型を含む一連のテスト用の型を定義し、これらの型の情報を表示する方法を示します。

この例では、型引数の配列を作成し、MakeGenericType メソッドを呼び出して、Dictionary<TKey,TValue> クラスから型を構築します。 プログラムでは、MakeGenericType を使用して構築された Type オブジェクトと、typeof (Visual Basic では GetType) を使用して取得した Type オブジェクトを比較し、これらが同じであることを示します。 同様に、GetGenericTypeDefinition メソッドを使用して構築された型のジェネリック型の定義を取得し、Dictionary<TKey,TValue> クラスを表す Type オブジェクトと比較します。

C#
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
{
    // 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.");
            }
        }
    }

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

関連項目