System.Type.MakeGenericType メソッド
この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。
この MakeGenericType メソッドを使用すると、ジェネリック型定義の型パラメーターに特定の型を割り当てるコードを記述できるため、特定の構築された型を Type 表すオブジェクトを作成できます。 この Type オブジェクトを使用して、構築された型のランタイム インスタンスを作成できます。
で構築された MakeGenericType 型は開くことができます。つまり、型引数の一部は、ジェネリック メソッドまたは型を囲む型パラメーターにすることができます。 このようなオープン構築型は、動的アセンブリを出力するときに使用できます。 たとえば、クラス Base
と Derived
次のコードについて考えてみます。
public class Base<T, U> { }
public class Derived<V> : Base<int, V> { }
type Base<'T, 'U>() = class end
type Derived<'V>() = inherit Base<int, 'V>()
Public Class Base(Of T, U)
End Class
Public Class Derived(Of V)
Inherits Base(Of Integer, V)
End Class
動的アセンブリで生成 Derived
するには、その基本型を構築する必要があります。 これを行うには、ジェネリック型引数と型パラメーターを使用して、クラスBase
を表すオブジェクトに対Typeしてメソッドを呼び出MakeGenericTypeしますV
Derived
。Int32 型パラメーターとジェネリック型パラメーターはどちらもオブジェクトによって Type 表されるため、両方を含む配列をメソッドに MakeGenericType 渡すことができます。
Note
このような Base<int, V>
構築型はコードを出力するときに便利ですが、ジェネリック型定義ではないため、この型でメソッドを呼び出 MakeGenericType すことはできません。 インスタンス化できる閉じた構築型を作成するには、まずメソッドを GetGenericTypeDefinition 呼び出してジェネリック型定義を表すオブジェクトを取得 Type し、次に目的の型引数を使用して呼び出 MakeGenericType します。
返されるオブジェクトはType、結果として生成されるMakeGenericType構築された型のメソッドを呼び出GetTypeした場合と同じか、同じ型引数をGetType使用して同じジェネリック型定義から作成された構築済み型のメソッドと同じですType。
Note
ジェネリック型の配列自体がジェネリック型ではありません。 (Visual Basic では)Dim ac() As C(Of T)
などのC<T>[]
配列型を呼び出MakeGenericTypeすことはできません。 閉じたジェネリック型 C<T>[]
を構築するには、ジェネリック型定義を取得する呼び出し GetElementType 、ジェネリック型定義 C<T>
を呼び出 MakeGenericType して構築された型を作成し、最後に構築された型のメソッドを呼び出 MakeArrayType して配列型を作成します。 ポインターの型と ref
型 (ByRef
Visual Basic の場合) についても同様です。
ジェネリック リフレクションで使用する用語に関する一定の条件の一覧については、IsGenericType プロパティの解説を参照してください。
入れ子にされた型
ジェネリック型が C#、C++、または Visual Basic を使用して定義されている場合、その入れ子になった型はすべてジェネリックになります。 これは、入れ子になった型に独自の型パラメーターがない場合でも当てはまります。3 つの言語はすべて、入れ子になった型の型パラメーター リストに、外側の型の型パラメーターが含まれているためです。 次のクラスについて考えてみましょう。
public class Outermost<T>
{
public class Inner<U>
{
public class Innermost1<V> {}
public class Innermost2 {}
}
}
Public Class Outermost(Of T)
Public Class Inner(Of U)
Public Class Innermost1(Of V)
End Class
Public Class Innermost2
End Class
End Class
End Class
入れ子になったクラスInner
の型パラメーター リストには 2 つの型パラメーターがありU
、 T
1 つ目は外側のクラスの型パラメーターです。 同様に、入れ子になったクラスの型パラメーター リストには、T
外側のクラスInnermost1
との間T
U
に 3 つの型パラメーター 、U
、および V
、および入れ子になったクラスからの型パラメーターがあります。 入れ子になったクラスには、T
外側のクラスInnermost2
から取得される 2 つの型パラメーターがありますU
。
外側の型のパラメーター リストに複数の型パラメーターがある場合、すべての型パラメーターが入れ子になった型の型パラメーター リストに順番に含まれます。
入れ子になった型のジェネリック型定義からジェネリック型を構築するには、最も外側のジェネリック型で始まり、入れ子になった型自体の型引数配列で終わる、外側のすべての型の型引数配列を連結して形成された配列を持つメソッドを呼び出 MakeGenericType します (独自の型パラメーターがある場合)。 インスタンス Innermost1
を作成するには、T、U、V に MakeGenericType 割り当てられる 3 つの型を含む配列を持つメソッドを呼び出します。インスタンス Innermost2
を作成するには、T と U に MakeGenericType 割り当てる 2 つの型を含む配列を持つメソッドを呼び出します。
言語は、外側の型の型パラメーターをこの方法で伝達するため、外側の型の型パラメーターを使用して、入れ子になった型のフィールドを定義できます。 それ以外の場合、型パラメーターは入れ子になった型の本体内のスコープ内にありません。 動的アセンブリでコードを出力するか、Ilasm.exe (IL アセンブラー) を使用して、外側の型の型パラメーターを伝達せずに、入れ子になった型を定義できます。 CIL アセンブラーの次のコードを考えてみましょう。
.class public Outer<T> {
.class nested public Inner<U> {
.class nested public Innermost {
}
}
}
この例では、型パラメーターがスコープ内にないため、型 T
または U
クラス Innermost
のフィールドを定義することはできません。 次のアセンブラー コードでは、C++、Visual Basic、C# で定義した場合と同様に動作する入れ子になったクラスを定義します。
.class public Outer<T> {
.class nested public Inner<T, U> {
.class nested public Innermost<T, U, V> {
}
}
}
Ildasm.exe (IL 逆アセンブラー) を使用して、高レベル言語で定義されている入れ子になったクラスを調べ、この名前付けスキームを確認できます。
.NET