方法 : リフレクションを使用してジェネリック型をチェックおよびインスタンス化する
更新 : 2007 年 11 月
ジェネリック型の情報は、他の型の情報と同じ方法で取得します。つまり、ジェネリック型を表す Type オブジェクトをチェックすることによって取得します。根本的な違いとして、ジェネリック型には、そのジェネリック型パラメータを表す Type オブジェクトの一覧が含まれています。このセクションでは、まず、ジェネリック型をチェックする手順を示します。
ジェネリック型の定義の型パラメータに型引数をバインドすることにより、構築された型を表す Type オブジェクトを作成できます。2 番目の手順でこの方法を示します。
ジェネリック型とその型パラメータをチェックするには
ジェネリック型を表す Type のインスタンスを取得します。次のコードでは、C# の typeof 演算子 (Visual Basic では GetType、Visual C++ では typeid) を使用して型を取得します。Type オブジェクトを取得する他の方法については、Type クラスのトピックを参照してください。この手順では、これ以降、型は t という名前のメソッド パラメータに格納されます。
Dim d1 As Type = GetType(Dictionary(Of ,))
Type d1 = typeof(Dictionary<,>);
IsGenericType プロパティを使用して、型がジェネリックかどうかを確認し、IsGenericTypeDefinition プロパティを使用して型がジェネリック型の定義かどうかを確認します。
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? {0}", t.IsGenericType); Console.WriteLine(" Is this a generic type definition? {0}", t.IsGenericTypeDefinition);
GetGenericArguments メソッドを使用して、ジェネリック型引数が格納された配列を取得します。
Dim typeParameters() As Type = t.GetGenericArguments()
Type[] typeParameters = t.GetGenericArguments();
各型引数について、IsGenericParameter プロパティを使用して、その型引数が型パラメータ (ジェネリック型定義の型パラメータなど) であるか、型パラメータに指定された型 (構築された型の型パラメータなど) であるかを確認します。
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
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); } }
型システムでは、ジェネリック型パラメータは、通常の型と同様に Type のインスタンスによって表されます。次のコードでは、ジェネリック型パラメータを表す Type オブジェクトの名前とパラメータ位置を表示します。パラメータ位置はここでは重要な情報ではありませんが、別のジェネリック型の型引数として使用されている型パラメータをチェックする場合に重要となります。
Private Shared Sub DisplayGenericParameter(ByVal tp As Type) Console.WriteLine(" Type parameter: {0} position {1}", _ tp.Name, tp.GenericParameterPosition)
private static void DisplayGenericParameter(Type tp) { Console.WriteLine(" Type parameter: {0} position {1}", tp.Name, tp.GenericParameterPosition);
GetGenericParameterConstraints メソッドを使用して、ジェネリック型パラメータの基本型の制約とインターフェイスの制約を確認し、単一の配列内のすべての制約を取得します。制約は、特定の順序であるという保証はありません。
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
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 プロパティを使用して、型パラメータの特殊な制約 (参照型であることが必要など) を確認します。このプロパティには、分散を表す値も含まれています。次のコードに示すように、この値にはマスクを設定できます。
Dim sConstraints As GenericParameterAttributes = _ tp.GenericParameterAttributes And _ GenericParameterAttributes.SpecialConstraintMask
GenericParameterAttributes sConstraints = tp.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
特殊な制約の属性はフラグであり、特殊な制約がないことを表すフラグ (GenericParameterAttributes.None) と同じフラグが、共変性や反変性がないことも表します。したがって、これらの条件のいずれかを調べるには、適切なマスクを使用する必要があります。ここでは、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
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 メソッドが必要です。
ジェネリック型のインスタンスを構築するには
ジェネリック型を表す Type オブジェクトを取得します。次のコードでは、2 とおりの方法でジェネリック型 Dictionary<TKey, TValue> を取得します。型を示す文字列を指定して Type.GetType(String) メソッド オーバーロードを使用する方法と、構築された型 Dictionary<String, Example> (Visual Basic では Dictionary(Of String, Example)) で GetGenericTypeDefinition メソッドを呼び出す方法です。MakeGenericType メソッドには、ジェネリック型の定義が必要です。
' 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()
// 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();
型引数の配列を構築して、型パラメータと置き換えます。配列には、型パラメータ リストに表示される順序と同じ順序で、正確な数の Type オブジェクトを格納する必要があります。この場合、キー (最初の型パラメータ) の型は String であり、ディクショナリの値は Example というクラスのインスタンスです。
Dim typeArgs() As Type = _ { GetType(String), GetType(Example) }
Type[] typeArgs = {typeof(string), typeof(Example)};
MakeGenericType メソッドを呼び出して型引数を型パラメータにバインドし、型を構築します。
Dim constructed As Type = _ d1.MakeGenericType(typeArgs)
Type constructed = d1.MakeGenericType(typeArgs);
CreateInstance(Type) メソッド オーバーロードを使用して、構築された型のオブジェクトを作成します。次のコードでは、作成された Dictionary<String, Example> オブジェクトに、Example クラスの 2 つのインスタンスを格納します。
Dim o As Object = Activator.CreateInstance(constructed)
object o = Activator.CreateInstance(constructed);
使用例
DisplayGenericType メソッドを定義して、コードで使用されているジェネリック型の定義と構築された型をチェックし、この情報を表示するコード例を次に示します。DisplayGenericType メソッドは、IsGenericType、IsGenericParameter、GenericParameterPosition の各プロパティと GetGenericArguments メソッドの使用方法を示します。
この例では、DisplayGenericParameter メソッドも定義してジェネリック型パラメータをチェックし、その制約を表示します。
このコード例では、型パラメータの制約を示すジェネリック型を含む一連のテスト用の型を定義し、これらの型の情報を表示する方法を示します。
この例では、型引数の配列を作成し、MakeGenericType メソッドを呼び出して、Dictionary<TKey, TValue> クラスから型を構築します。プログラムでは、MakeGenericType を使用して構築された Type オブジェクトと、typeof (Visual Basic では GetType) を使用して取得した Type オブジェクトを比較し、これらが同じであることを示します。同様に、GetGenericTypeDefinition メソッドを使用して構築された型のジェネリック型の定義を取得し、Dictionary<TKey, TValue> クラスを表す Type オブジェクトと比較します。
Imports System
Imports System.Reflection
Imports System.Collections.Generic
Imports System.Security.Permissions
' 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
<PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
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
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<>));
}
}
コードのコンパイル方法
このコードには、コンパイルに必要な C# の using ステートメント (Visual Basic では Imports) が含まれています。
追加のアセンブリ参照は不要です。
csc.exe、vbc.exe、または cl.exe を使用して、コマンド ラインでコードをコンパイルします。Visual Studio でコードをコンパイルするには、コンソール アプリケーション プロジェクト テンプレートにコードを配置します。