リフレクションとジェネリック型

リフレクションの観点から言えば、ジェネリック型は、それがジェネリック型定義である場合は型パラメーター セットが、構築された型である場合は型引数セットが関連付けられているという点で通常の型と異なります。 ジェネリック メソッドと通常のメソッドの違いも、それと同様です。

リフレクションでジェネリック型とジェネリック メソッドが処理されるしくみを理解するうえで、次の 2 つの点が重要となります。

  • ジェネリック型の定義とジェネリック メソッドの定義の型パラメーターは、 Type クラスのインスタンスによって表されます。

    Note

    Type オブジェクトがジェネリック型パラメーターを表す場合、 Type の多数のプロパティとメソッドの動作が異なります。 これらの相違点については、該当するプロパティおよびメソッドに関する記事に記載されています。 たとえば、 IsAutoClassDeclaringTypeを参照してください。 さらに、一部のメンバーは Type オブジェクトがジェネリック型パラメーターを表す場合にのみ有効です。 例については、「 GetGenericTypeDefinition」を参照してください。

  • Type のインスタンスがジェネリック型を表す場合、そのインスタンスには、型パラメーター (ジェネリック型の定義の場合) または型引数 (構築された型の場合) を表す型の配列が含まれます。 ジェネリック メソッドを表す MethodInfo クラスのインスタンスの場合も同様です。

リフレクションが提供する Type および MethodInfo のメソッドを使用すると、型パラメーターの配列にアクセスしたり、Type のインスタンスが型パラメーターと実際の型のどちらを表しているかを確認したりできます。

ここで説明したメソッドを示すコード例については、「方法:リフレクションを使用してジェネリック型をチェックおよびインスタンス化する」に記載された追加情報を参照してください。

以下の説明は、型パラメーターと型引数の違いや、オープン構築型とクローズ構築型の違いなど、ジェネリックの用語を十分に理解していることを前提としています。 詳細については、「ジェネリック」を参照してください。

ジェネリック型またはジェネリック メソッドであるかどうか

リフレクションを使用して、 Typeのインスタンスによって表される不明な型を調べる場合は、 IsGenericType プロパティを使用してその不明な型がジェネリックかどうかを確認します。 型がジェネリックの場合、 true を返します。 同様に、 MethodInfo クラスのインスタンスによって表される不明なメソッドを調べる場合には、 IsGenericMethod プロパティを使用してそのメソッドがジェネリックかどうかを確認します。

ジェネリック型またはジェネリック メソッドの定義であるかどうか

IsGenericTypeDefinition オブジェクトがジェネリック型の定義を表しているかどうかを確認するには、 Type プロパティを使用します。また、 IsGenericMethodDefinition がジェネリック メソッドの定義を表しているかどうかを確認するには、 MethodInfo メソッドを使用します。

ジェネリック型の定義とジェネリック メソッドの定義は、インスタンス化可能な型の作成元となるテンプレートです。 .NET ライブラリ内のジェネリック型 (Dictionary<TKey,TValue> など) は、ジェネリック型定義です。

型またはメソッドがオープンかクローズか

すべての型パラメーター (すべての内包する型のすべての型パラメーターを含む) がインスタンス化可能な型に置き換えられている場合、ジェネリック型またはジェネリック メソッドはクローズであるといいます。 ジェネリック型のインスタンスを作成できるのは、それがクローズである場合だけです。 型がオープンである場合、 Type.ContainsGenericParameters プロパティは true を返します。 メソッドの場合、 MethodBase.ContainsGenericParameters メソッドで同じ機能が実行されます。

クローズ ジェネリック型を生成する

ジェネリック型の定義を取得したら、 MakeGenericType メソッドを使用してクローズ ジェネリック型を作成します。また、ジェネリック メソッドの定義を取得したら、 MakeGenericMethod メソッドを使用してクローズ ジェネリック メソッドの MethodInfo を作成します。

ジェネリック型またはメソッドの定義の取得

ジェネリック型またはメソッドの定義ではないオープン ジェネリック型またはメソッドがある場合、そのインスタンスを作成することはできず、欠落している型パラメーターを指定することもできません。 ジェネリック型の定義またはジェネリック メソッドの定義が必要です。 ジェネリック型の定義を取得するには GetGenericTypeDefinition メソッドを使用し、ジェネリック メソッドの定義を取得するには GetGenericMethodDefinition メソッドを使用します。

たとえば、Dictionary<int, string> を表す Type オブジェクトがあり、型 Dictionary<string, MyClass>を作成する場合は、GetGenericTypeDefinition メソッドを使用して Dictionary<TKey, TValue> を表す Type を取得し、MakeGenericType メソッドを使用して Dictionary<int, MyClass> を表す Typeを生成できます。

ジェネリック型ではないオープン ジェネリック型の例は、「型パラメーターまたは型引数」を参照してください。

型引数と型パラメーターの確認

ジェネリック型の型パラメーターまたは型引数を表す Type.GetGenericArguments オブジェクトの配列を取得するには、 Type メソッドを使用します。また、ジェネリック メソッドに対して同じ操作を実行するには、 MethodInfo.GetGenericArguments メソッドを使用します。

Type オブジェクトが型パラメーターを表していることがわかったら、リフレクションによって他の多くの詳細を確認できます 型パラメーターのソース、その位置、およびその制約を確認できます。

型パラメーターまたは型引数

配列の特定の要素が型パラメーターと型引数のどちらであるかを確認するには、 IsGenericParameter プロパティを使用します。 要素が型パラメーターの場合、 IsGenericParameter プロパティは true です。

ジェネリック型には、ジェネリック型の定義がないことがあります。その場合のジェネリック型はオープンであり、型引数と型パラメーターが混在しています。 たとえば、次のコードでは、クラス DD の 2 つ目の型パラメーターを Bの 1 つ目の型パラメーターに置き換えることによって作成された型から派生します。

class B<T, U> {}
class D<V, W> : B<int, V> {}
Class B(Of T, U)
End Class
Class D(Of V, W)
    Inherits B(Of Integer, V)
End Class
generic<typename T, typename U> ref class B {};
generic<typename V, typename W> ref class D : B<int, V> {};

D<V, W> を表す Type オブジェクトを取得し、BaseType プロパティを使用してその基本データ型を取得すると、結果の type B<int, V> は開きますが、ジェネリック型定義ではありません。

ジェネリック パラメーターのソース

ジェネリック型パラメーターのソースとしては、開発者が今調べている型、外側の型、またはジェネリック メソッドが考えられます。 ジェネリック型パラメーターのソースを特定するには、次の手順を実行します。

  • まず、 DeclaringMethod プロパティを使用して、型パラメーターのソースがジェネリック メソッドかどうかを判定します。 このプロパティ値が null 参照ではない場合、ソースはジェネリック メソッドです。
  • ソースがジェネリック メソッドでない場合は、 DeclaringType プロパティを使用して、ジェネリック型パラメーターが属しているジェネリック型を判定します。

型パラメーターがジェネリック メソッドに属している場合、 DeclaringType プロパティはそのジェネリック メソッドを宣言した型を返しますが、これは無関係です。

ジェネリック パラメーターの位置

まれなケースですが、宣言するクラスの型パラメーター リスト内の型パラメーターの位置を確認することが必要な場合があります。 たとえば、上の例の Type 型を表す B<int, V> オブジェクトについて考えてみます。 GetGenericArguments メソッドからは、型引数のリストが提供されます。 V を調べるときには、 DeclaringMethod プロパティと DeclaringType プロパティを使用してそれがどこからのものかを確認できます 次に、 GenericParameterPosition プロパティを使用することにより、型パラメーター リストの中でそれが定義されていた位置を判定できます。 この例では、 V は、定義された型パラメーター リスト内の 0 (ゼロ) の位置にあります。

基本データ型とインターフェイスの制約

GetGenericParameterConstraints メソッドを使用すると、基本データ型の制約と型パラメーターのインターフェイス制約を取得できます。 配列の要素の順序は重要ではありません。 要素がインターフェイス型の場合、それはインターフェイスの制約を表します。

ジェネリック パラメーターの属性

GenericParameterAttributes プロパティを使用すると、型パラメーターの変化 (共変性または反変性) と特殊な制約を示す GenericParameterAttributes 値を取得できます。

共変性と反変性

型パラメーターが共変性と反変性のどちらであるかを判定するには、 GenericParameterAttributes.VarianceMask プロパティから返される GenericParameterAttributes 値に GenericParameterAttributes マスクを適用します。 結果が GenericParameterAttributes.Noneの場合、型パラメーターはインバリアント (不変) です。 詳細については、「共変性と反変性」を参照してください。

特殊な制約

型パラメーターの特殊な制約を判定するには、 GenericParameterAttributes.SpecialConstraintMask プロパティから返される GenericParameterAttributes 値に GenericParameterAttributes マスクを適用します。 結果が GenericParameterAttributes.Noneの場合、特殊な制約はありません。 型パラメーターには、参照型であること、null 非許容値型であること、およびパラメーターなしのコンストラクターを持っていること、という制約が可能です。

インバリアント

ジェネリック型のリフレクションで使用される一般的な用語に対するインバリアント条件を記載した表は、 Type.IsGenericTypeを参照してください。 ジェネリック メソッドに関連するその他の用語については、 MethodBase.IsGenericMethodを参照してください。