この記事では、2 つの型パラメーターを持つ単純なジェネリック型を作成する方法、クラス制約、インターフェイス制約、および特殊制約を型パラメーターに適用する方法、およびクラスの型パラメーターをパラメーター型および戻り値の型として使用するメンバーを作成する方法について説明します。
重要
メソッドはジェネリック型に属し、その型の型パラメーターを使用するため、ジェネリックではありません。 メソッドは、独自の型パラメーター リストがある場合にのみジェネリックです。 この例のように、ジェネリック型のほとんどのメソッドはジェネリックではありません。 ジェネリック メソッドを出力する例については、「 方法: リフレクション出力を使用してジェネリック メソッドを定義する」を参照してください。
ジェネリック型を定義する
GenericEmitExample1
という名前の動的アセンブリを定義します。 この例では、アセンブリが実行され、ディスクに保存されるため、 AssemblyBuilderAccess.RunAndSave が指定されています。AppDomain myDomain = AppDomain.CurrentDomain; AssemblyName myAsmName = new AssemblyName("GenericEmitExample1"); AssemblyBuilder myAssembly = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
Dim myDomain As AppDomain = AppDomain.CurrentDomain Dim myAsmName As New AssemblyName("GenericEmitExample1") Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _ myAsmName, _ AssemblyBuilderAccess.RunAndSave)
動的モジュールを定義します。 アセンブリは実行可能モジュールで構成されます。 単一モジュール アセンブリの場合、モジュール名はアセンブリ名と同じであり、ファイル名はモジュール名と拡張子です。
ModuleBuilder myModule = myAssembly.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _ myAsmName.Name, _ myAsmName.Name & ".dll")
クラスを定義します。 この例では、クラスの名前は
Sample
です。TypeBuilder myType = myModule.DefineType("Sample", TypeAttributes.Public);
Dim myType As TypeBuilder = myModule.DefineType( _ "Sample", _ TypeAttributes.Public)
パラメーターの名前を含む文字列の配列を
Sample
メソッドに渡して、TypeBuilder.DefineGenericParametersのジェネリック型パラメーターを定義します。 これにより、クラスがジェネリック型になります。 戻り値は、出力されたコードで使用できる型パラメーターを表す GenericTypeParameterBuilder オブジェクトの配列です。次のコードでは、
Sample
がTFirst
とTSecond
を型パラメーターとして持つジェネリック型になります。 コードを読みやすくするために、各 GenericTypeParameterBuilder は型パラメーターと同じ名前の変数に配置されます。string[] typeParamNames = {"TFirst", "TSecond"}; GenericTypeParameterBuilder[] typeParams = myType.DefineGenericParameters(typeParamNames); GenericTypeParameterBuilder TFirst = typeParams[0]; GenericTypeParameterBuilder TSecond = typeParams[1];
Dim typeParamNames() As String = {"TFirst", "TSecond"} Dim typeParams() As GenericTypeParameterBuilder = _ myType.DefineGenericParameters(typeParamNames) Dim TFirst As GenericTypeParameterBuilder = typeParams(0) Dim TSecond As GenericTypeParameterBuilder = typeParams(1)
型パラメーターに特別な制約を追加します。 この例では、型パラメーター
TFirst
は、パラメーターなしのコンストラクターを持つ型と参照型に制約されます。TFirst.SetGenericParameterAttributes( GenericParameterAttributes.DefaultConstructorConstraint | GenericParameterAttributes.ReferenceTypeConstraint);
TFirst.SetGenericParameterAttributes( _ GenericParameterAttributes.DefaultConstructorConstraint _ Or GenericParameterAttributes.ReferenceTypeConstraint)
必要に応じて、クラスとインターフェイスの制約を型パラメーターに追加します。 この例では、型パラメーター
TFirst
は、変数Typeに含まれるbaseType
オブジェクトによって表される基底クラスから派生し、その型が変数interfaceA
およびinterfaceB
に含まれるインターフェイスを実装する型に制約されます。 これらの変数の宣言と代入については、コード例を参照してください。TSecond.SetBaseTypeConstraint(baseType); Type[] interfaceTypes = {interfaceA, interfaceB}; TSecond.SetInterfaceConstraints(interfaceTypes);
TSecond.SetBaseTypeConstraint(baseType) Dim interfaceTypes() As Type = {interfaceA, interfaceB} TSecond.SetInterfaceConstraints(interfaceTypes)
フィールドを定義します。 この例では、フィールドの型は型パラメーター
TFirst
で指定されています。 GenericTypeParameterBuilder は Typeから派生しているため、型を使用できる任意の場所でジェネリック型パラメーターを使用できます。FieldBuilder exField = myType.DefineField("ExampleField", TFirst, FieldAttributes.Private);
Dim exField As FieldBuilder = _ myType.DefineField("ExampleField", TFirst, _ FieldAttributes.Private)
ジェネリック型の型パラメーターを使用するメソッドを定義します。 このようなメソッドは、独自の型パラメーター リストがない限り、ジェネリックではないことに注意してください。 次のコードでは、
static
の配列を受け取り、配列のすべての要素を含むShared
(Visual Basic のTFirst
) を返すList<TFirst>
メソッド (Visual Basic のList(Of TFirst)
) を定義します。 このメソッドを定義するには、ジェネリック型定義 (List<TFirst>
) でMakeGenericTypeを呼び出して、型List<T>
を作成する必要があります。 (T
演算子 (Visual Basic ではtypeof
) を使用してジェネリック型定義を取得する場合、GetType
は省略されます)。 パラメーター型は、 MakeArrayType メソッドを使用して作成されます。Type listOf = typeof(List<>); Type listOfTFirst = listOf.MakeGenericType(TFirst); Type[] mParamTypes = {TFirst.MakeArrayType()}; MethodBuilder exMethod = myType.DefineMethod("ExampleMethod", MethodAttributes.Public | MethodAttributes.Static, listOfTFirst, mParamTypes);
Dim listOf As Type = GetType(List(Of )) Dim listOfTFirst As Type = listOf.MakeGenericType(TFirst) Dim mParamTypes() As Type = {TFirst.MakeArrayType()} Dim exMethod As MethodBuilder = _ myType.DefineMethod("ExampleMethod", _ MethodAttributes.Public Or MethodAttributes.Static, _ listOfTFirst, _ mParamTypes)
メソッド本体を出力します。 メソッド本体は、入力配列をスタックに読み込み、
List<TFirst>
を受け取るIEnumerable<TFirst>
コンストラクターを呼び出し (入力要素をリストに配置するすべての処理を行います)、戻る (新しいList<T> オブジェクトをスタックに残す) 3 つのオペコードで構成されます。 このコードを出力する難しい部分は、コンストラクターを取得することです。GetConstructor メソッドはGenericTypeParameterBuilderではサポートされていないため、
List<TFirst>
のコンストラクターを直接取得することはできません。 まず、ジェネリック型定義List<T>
のコンストラクターを取得してから、それをList<TFirst>
の対応するコンストラクターに変換するメソッドを呼び出す必要があります。このコード例で使用されるコンストラクターは、
IEnumerable<T>
を受け取ります。 ただし、これはIEnumerable<T>ジェネリック インターフェイスのジェネリック型定義ではないことに注意してください。代わりに、T
からList<T>
型パラメーターは、T
の型パラメーターIEnumerable<T>
に置き換える必要があります。 (これは、両方の型にT
という名前の型パラメーターがあるため、混乱を招くようです。そのため、このコード例では名前TFirst
とTSecond
を使用します)。コンストラクター引数の型を取得するには、ジェネリック型定義IEnumerable<T>
から開始し、MakeGenericTypeの最初のジェネリック型パラメーターでList<T>
を呼び出します。 コンストラクター引数リストは配列として渡す必要があります。この場合、引数は 1 つだけです。注
ジェネリック型の定義は、C# で
IEnumerable<>
演算子を使用する場合はtypeof
、Visual Basic でIEnumerable(Of )
演算子を使用する場合はGetType
として表されます。ジェネリック型定義で
List<T>
を呼び出すことで、GetConstructorのコンストラクターを取得できるようになりました。 このコンストラクターをList<TFirst>
の対応するコンストラクターに変換するには、List<TFirst>
とコンストラクターをList<T>
から静的 TypeBuilder.GetConstructor(Type, ConstructorInfo) メソッドに渡します。ILGenerator ilgen = exMethod.GetILGenerator(); Type ienumOf = typeof(IEnumerable<>); Type TfromListOf = listOf.GetGenericArguments()[0]; Type ienumOfT = ienumOf.MakeGenericType(TfromListOf); Type[] ctorArgs = {ienumOfT}; ConstructorInfo ctorPrep = listOf.GetConstructor(ctorArgs); ConstructorInfo ctor = TypeBuilder.GetConstructor(listOfTFirst, ctorPrep); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Newobj, ctor); ilgen.Emit(OpCodes.Ret);
Dim ilgen As ILGenerator = exMethod.GetILGenerator() Dim ienumOf As Type = GetType(IEnumerable(Of )) Dim listOfTParams() As Type = listOf.GetGenericArguments() Dim TfromListOf As Type = listOfTParams(0) Dim ienumOfT As Type = ienumOf.MakeGenericType(TfromListOf) Dim ctorArgs() As Type = {ienumOfT} Dim ctorPrep As ConstructorInfo = _ listOf.GetConstructor(ctorArgs) Dim ctor As ConstructorInfo = _ TypeBuilder.GetConstructor(listOfTFirst, ctorPrep) ilgen.Emit(OpCodes.Ldarg_0) ilgen.Emit(OpCodes.Newobj, ctor) ilgen.Emit(OpCodes.Ret)
型を作成し、ファイルを保存します。
Type finished = myType.CreateType(); myAssembly.Save(myAsmName.Name+".dll");
Dim finished As Type = myType.CreateType() myAssembly.Save(myAsmName.Name & ".dll")
メソッドを呼び出します。
ExampleMethod
はジェネリックではありませんが、属する型はジェネリックであるため、呼び出すことができる MethodInfo を取得するには、Sample
の型定義から構築された型を作成する必要があります。 構築された型は、Example
クラスを使用します。これは、参照型であり、既定のパラメーターなしのコンストラクターと、TFirst
の制約を満たすExampleDerived
クラスがあるため、TSecond
の制約を満たします。 (ExampleDerived
のコードは、コード例セクションにあります)。これら 2 つの型は、構築された型を作成するために MakeGenericType に渡されます。 次に、MethodInfoメソッドを使用してGetMethodを取得します。Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)}; Type constructed = finished.MakeGenericType(typeArgs); MethodInfo mi = constructed.GetMethod("ExampleMethod");
Dim typeArgs() As Type = _ {GetType(Example), GetType(ExampleDerived)} Dim constructed As Type = finished.MakeGenericType(typeArgs) Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
次のコードでは、
Example
オブジェクトの配列を作成し、その配列を呼び出すメソッドの引数を表す Object 型の配列に配置し、 Invoke(Object, Object[]) メソッドに渡します。 メソッドがInvokeされているため、static
メソッドの最初の引数は null 参照です。Example[] input = {new Example(), new Example()}; object[] arguments = {input}; List<Example> listX = (List<Example>) mi.Invoke(null, arguments); Console.WriteLine($"\nThere are {listX.Count} elements in the List<Example>.");
Dim input() As Example = {New Example(), New Example()} Dim arguments() As Object = {input} Dim listX As List(Of Example) = mi.Invoke(Nothing, arguments) Console.WriteLine(vbLf & _ "There are {0} elements in the List(Of Example).", _ listX.Count _ )
例
次のコード例では、基底クラスと 2 つのインターフェイスと共に、 Sample
という名前のクラスを定義します。 このプログラムは、 Sample
の 2 つのジェネリック型パラメーターを定義し、ジェネリック型に変換します。 型パラメーターは、型をジェネリックにする唯一のものです。 プログラムは、型パラメーターの定義の前後にテスト メッセージを表示することで、これを示します。
TSecond
型パラメーターは、基底クラスとインターフェイスを使用してクラスとインターフェイスの制約を示すために使用され、TFirst
型パラメーターを使用して特別な制約を示します。
このコード例では、フィールド型、およびメソッドのパラメーターと戻り値の型に対してクラスの型パラメーターを使用して、フィールドとメソッドを定義します。
Sample
クラスが作成されると、メソッドが呼び出されます。
このプログラムには、ジェネリック型に関する情報を一覧表示するメソッドと、型パラメーターに対する特別な制約を一覧表示するメソッドが含まれます。 これらのメソッドは、完成した Sample
クラスに関する情報を表示するために使用されます。
完成したモジュールを GenericEmitExample1.dll
としてディスクに保存するため、 Ildasm.exe (IL 逆アセンブラー) で開き、 Sample
クラスの CIL を調べることができます。
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
// Define a trivial base class and two trivial interfaces
// to use when demonstrating constraints.
//
public class ExampleBase {}
public interface IExampleA {}
public interface IExampleB {}
// Define a trivial type that can substitute for type parameter
// TSecond.
//
public class ExampleDerived : ExampleBase, IExampleA, IExampleB {}
public class Example
{
public static void Main()
{
// Define a dynamic assembly to contain the sample type. The
// assembly will not be run, but only saved to disk, so
// AssemblyBuilderAccess.Save is specified.
//
AppDomain myDomain = AppDomain.CurrentDomain;
AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
AssemblyBuilder myAssembly =
myDomain.DefineDynamicAssembly(myAsmName,
AssemblyBuilderAccess.RunAndSave);
// An assembly is made up of executable modules. For a single-
// module assembly, the module name and file name are the same
// as the assembly name.
//
ModuleBuilder myModule =
myAssembly.DefineDynamicModule(myAsmName.Name,
myAsmName.Name + ".dll");
// Get type objects for the base class trivial interfaces to
// be used as constraints.
//
Type baseType = typeof(ExampleBase);
Type interfaceA = typeof(IExampleA);
Type interfaceB = typeof(IExampleB);
// Define the sample type.
//
TypeBuilder myType =
myModule.DefineType("Sample", TypeAttributes.Public);
Console.WriteLine($"Type 'Sample' is generic: {myType.IsGenericType}");
// Define type parameters for the type. Until you do this,
// the type is not generic, as the preceding and following
// WriteLine statements show. The type parameter names are
// specified as an array of strings. To make the code
// easier to read, each GenericTypeParameterBuilder is placed
// in a variable with the same name as the type parameter.
//
string[] typeParamNames = {"TFirst", "TSecond"};
GenericTypeParameterBuilder[] typeParams =
myType.DefineGenericParameters(typeParamNames);
GenericTypeParameterBuilder TFirst = typeParams[0];
GenericTypeParameterBuilder TSecond = typeParams[1];
Console.WriteLine($"Type 'Sample' is generic: {myType.IsGenericType}");
// Apply constraints to the type parameters.
//
// A type that is substituted for the first parameter, TFirst,
// must be a reference type and must have a parameterless
// constructor.
TFirst.SetGenericParameterAttributes(
GenericParameterAttributes.DefaultConstructorConstraint |
GenericParameterAttributes.ReferenceTypeConstraint);
// A type that is substituted for the second type
// parameter must implement IExampleA and IExampleB, and
// inherit from the trivial test class ExampleBase. The
// interface constraints are specified as an array
// containing the interface types.
TSecond.SetBaseTypeConstraint(baseType);
Type[] interfaceTypes = {interfaceA, interfaceB};
TSecond.SetInterfaceConstraints(interfaceTypes);
// The following code adds a private field named ExampleField,
// of type TFirst.
FieldBuilder exField =
myType.DefineField("ExampleField", TFirst,
FieldAttributes.Private);
// Define a static method that takes an array of TFirst and
// returns a List<TFirst> containing all the elements of
// the array. To define this method it is necessary to create
// the type List<TFirst> by calling MakeGenericType on the
// generic type definition, List<T>. (The T is omitted with
// the typeof operator when you get the generic type
// definition.) The parameter type is created by using the
// MakeArrayType method.
//
Type listOf = typeof(List<>);
Type listOfTFirst = listOf.MakeGenericType(TFirst);
Type[] mParamTypes = {TFirst.MakeArrayType()};
MethodBuilder exMethod =
myType.DefineMethod("ExampleMethod",
MethodAttributes.Public | MethodAttributes.Static,
listOfTFirst,
mParamTypes);
// Emit the method body.
// The method body consists of just three opcodes, to load
// the input array onto the execution stack, to call the
// List<TFirst> constructor that takes IEnumerable<TFirst>,
// which does all the work of putting the input elements into
// the list, and to return, leaving the list on the stack. The
// hard work is getting the constructor.
//
// The GetConstructor method is not supported on a
// GenericTypeParameterBuilder, so it is not possible to get
// the constructor of List<TFirst> directly. There are two
// steps, first getting the constructor of List<T> and then
// calling a method that converts it to the corresponding
// constructor of List<TFirst>.
//
// The constructor needed here is the one that takes an
// IEnumerable<T>. Note, however, that this is not the
// generic type definition of IEnumerable<T>; instead, the
// T from List<T> must be substituted for the T of
// IEnumerable<T>. (This seems confusing only because both
// types have type parameters named T. That is why this example
// uses the somewhat silly names TFirst and TSecond.) To get
// the type of the constructor argument, take the generic
// type definition IEnumerable<T> (expressed as
// IEnumerable<> when you use the typeof operator) and
// call MakeGenericType with the first generic type parameter
// of List<T>. The constructor argument list must be passed
// as an array, with just one argument in this case.
//
// Now it is possible to get the constructor of List<T>,
// using GetConstructor on the generic type definition. To get
// the constructor of List<TFirst>, pass List<TFirst> and
// the constructor from List<T> to the static
// TypeBuilder.GetConstructor method.
//
ILGenerator ilgen = exMethod.GetILGenerator();
Type ienumOf = typeof(IEnumerable<>);
Type TfromListOf = listOf.GetGenericArguments()[0];
Type ienumOfT = ienumOf.MakeGenericType(TfromListOf);
Type[] ctorArgs = {ienumOfT};
ConstructorInfo ctorPrep = listOf.GetConstructor(ctorArgs);
ConstructorInfo ctor =
TypeBuilder.GetConstructor(listOfTFirst, ctorPrep);
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Newobj, ctor);
ilgen.Emit(OpCodes.Ret);
// Create the type and save the assembly.
Type finished = myType.CreateType();
myAssembly.Save(myAsmName.Name+".dll");
// Invoke the method.
// ExampleMethod is not generic, but the type it belongs to is
// generic, so in order to get a MethodInfo that can be invoked
// it is necessary to create a constructed type. The Example
// class satisfies the constraints on TFirst, because it is a
// reference type and has a default constructor. In order to
// have a class that satisfies the constraints on TSecond,
// this code example defines the ExampleDerived type. These
// two types are passed to MakeGenericMethod to create the
// constructed type.
//
Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
Type constructed = finished.MakeGenericType(typeArgs);
MethodInfo mi = constructed.GetMethod("ExampleMethod");
// Create an array of Example objects, as input to the generic
// method. This array must be passed as the only element of an
// array of arguments. The first argument of Invoke is
// null, because ExampleMethod is static. Display the count
// on the resulting List<Example>.
//
Example[] input = {new Example(), new Example()};
object[] arguments = {input};
List<Example> listX =
(List<Example>) mi.Invoke(null, arguments);
Console.WriteLine($"\nThere are {listX.Count} elements in the List<Example>.");
DisplayGenericParameters(finished);
}
private static void DisplayGenericParameters(Type t)
{
if (!t.IsGenericType)
{
Console.WriteLine("Type '{0}' is not generic.");
return;
}
if (!t.IsGenericTypeDefinition)
{
t = t.GetGenericTypeDefinition();
}
Type[] typeParameters = t.GetGenericArguments();
Console.WriteLine($"\nListing {typeParameters.Length} type parameters for type '{t}'.");
foreach( Type tParam in typeParameters )
{
Console.WriteLine($"""
Type parameter {tParam.ToString()}:
""");
foreach( Type c in tParam.GetGenericParameterConstraints() )
{
if (c.IsInterface)
{
Console.WriteLine($" Interface constraint: {c}");
}
else
{
Console.WriteLine($" Base type constraint: {c}");
}
}
ListConstraintAttributes(tParam);
}
}
// List the constraint flags. The GenericParameterAttributes
// enumeration contains two sets of attributes, variance and
// constraints. For this example, only constraints are used.
//
private static void ListConstraintAttributes(Type t)
{
// Mask off the constraint flags.
GenericParameterAttributes constraints =
t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
if ((constraints & GenericParameterAttributes.ReferenceTypeConstraint)
!= GenericParameterAttributes.None)
{
Console.WriteLine(" ReferenceTypeConstraint");
}
if ((constraints & GenericParameterAttributes.NotNullableValueTypeConstraint)
!= GenericParameterAttributes.None)
{
Console.WriteLine(" NotNullableValueTypeConstraint");
}
if ((constraints & GenericParameterAttributes.DefaultConstructorConstraint)
!=GenericParameterAttributes.None)
{
Console.WriteLine(" DefaultConstructorConstraint");
}
}
}
/* This code example produces the following output:
Type 'Sample' is generic: False
Type 'Sample' is generic: True
There are 2 elements in the List<Example>.
Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.
Type parameter TFirst:
ReferenceTypeConstraint
DefaultConstructorConstraint
Type parameter TSecond:
Interface constraint: IExampleA
Interface constraint: IExampleB
Base type constraint: ExampleBase
*/
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Collections.Generic
' Define a trivial base class and two trivial interfaces
' to use when demonstrating constraints.
'
Public Class ExampleBase
End Class
Public Interface IExampleA
End Interface
Public Interface IExampleB
End Interface
' Define a trivial type that can substitute for type parameter
' TSecond.
'
Public Class ExampleDerived
Inherits ExampleBase
Implements IExampleA, IExampleB
End Class
Public Class Example
Public Shared Sub Main()
' Define a dynamic assembly to contain the sample type. The
' assembly will not be run, but only saved to disk, so
' AssemblyBuilderAccess.Save is specified.
'
Dim myDomain As AppDomain = AppDomain.CurrentDomain
Dim myAsmName As New AssemblyName("GenericEmitExample1")
Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
myAsmName, _
AssemblyBuilderAccess.RunAndSave)
' An assembly is made up of executable modules. For a single-
' module assembly, the module name and file name are the same
' as the assembly name.
'
Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
myAsmName.Name, _
myAsmName.Name & ".dll")
' Get type objects for the base class trivial interfaces to
' be used as constraints.
'
Dim baseType As Type = GetType(ExampleBase)
Dim interfaceA As Type = GetType(IExampleA)
Dim interfaceB As Type = GetType(IExampleB)
' Define the sample type.
'
Dim myType As TypeBuilder = myModule.DefineType( _
"Sample", _
TypeAttributes.Public)
Console.WriteLine("Type 'Sample' is generic: {0}", _
myType.IsGenericType)
' Define type parameters for the type. Until you do this,
' the type is not generic, as the preceding and following
' WriteLine statements show. The type parameter names are
' specified as an array of strings. To make the code
' easier to read, each GenericTypeParameterBuilder is placed
' in a variable with the same name as the type parameter.
'
Dim typeParamNames() As String = {"TFirst", "TSecond"}
Dim typeParams() As GenericTypeParameterBuilder = _
myType.DefineGenericParameters(typeParamNames)
Dim TFirst As GenericTypeParameterBuilder = typeParams(0)
Dim TSecond As GenericTypeParameterBuilder = typeParams(1)
Console.WriteLine("Type 'Sample' is generic: {0}", _
myType.IsGenericType)
' Apply constraints to the type parameters.
'
' A type that is substituted for the first parameter, TFirst,
' must be a reference type and must have a parameterless
' constructor.
TFirst.SetGenericParameterAttributes( _
GenericParameterAttributes.DefaultConstructorConstraint _
Or GenericParameterAttributes.ReferenceTypeConstraint)
' A type that is substituted for the second type
' parameter must implement IExampleA and IExampleB, and
' inherit from the trivial test class ExampleBase. The
' interface constraints are specified as an array
' containing the interface types.
TSecond.SetBaseTypeConstraint(baseType)
Dim interfaceTypes() As Type = {interfaceA, interfaceB}
TSecond.SetInterfaceConstraints(interfaceTypes)
' The following code adds a private field named ExampleField,
' of type TFirst.
Dim exField As FieldBuilder = _
myType.DefineField("ExampleField", TFirst, _
FieldAttributes.Private)
' Define a Shared method that takes an array of TFirst and
' returns a List(Of TFirst) containing all the elements of
' the array. To define this method it is necessary to create
' the type List(Of TFirst) by calling MakeGenericType on the
' generic type definition, List(Of T). (The T is omitted with
' the GetType operator when you get the generic type
' definition.) The parameter type is created by using the
' MakeArrayType method.
'
Dim listOf As Type = GetType(List(Of ))
Dim listOfTFirst As Type = listOf.MakeGenericType(TFirst)
Dim mParamTypes() As Type = {TFirst.MakeArrayType()}
Dim exMethod As MethodBuilder = _
myType.DefineMethod("ExampleMethod", _
MethodAttributes.Public Or MethodAttributes.Static, _
listOfTFirst, _
mParamTypes)
' Emit the method body.
' The method body consists of just three opcodes, to load
' the input array onto the execution stack, to call the
' List(Of TFirst) constructor that takes IEnumerable(Of TFirst),
' which does all the work of putting the input elements into
' the list, and to return, leaving the list on the stack. The
' hard work is getting the constructor.
'
' The GetConstructor method is not supported on a
' GenericTypeParameterBuilder, so it is not possible to get
' the constructor of List(Of TFirst) directly. There are two
' steps, first getting the constructor of List(Of T) and then
' calling a method that converts it to the corresponding
' constructor of List(Of TFirst).
'
' The constructor needed here is the one that takes an
' IEnumerable(Of T). Note, however, that this is not the
' generic type definition of IEnumerable(Of T); instead, the
' T from List(Of T) must be substituted for the T of
' IEnumerable(Of T). (This seems confusing only because both
' types have type parameters named T. That is why this example
' uses the somewhat silly names TFirst and TSecond.) To get
' the type of the constructor argument, take the generic
' type definition IEnumerable(Of T) (expressed as
' IEnumerable(Of ) when you use the GetType operator) and
' call MakeGenericType with the first generic type parameter
' of List(Of T). The constructor argument list must be passed
' as an array, with just one argument in this case.
'
' Now it is possible to get the constructor of List(Of T),
' using GetConstructor on the generic type definition. To get
' the constructor of List(Of TFirst), pass List(Of TFirst) and
' the constructor from List(Of T) to the static
' TypeBuilder.GetConstructor method.
'
Dim ilgen As ILGenerator = exMethod.GetILGenerator()
Dim ienumOf As Type = GetType(IEnumerable(Of ))
Dim listOfTParams() As Type = listOf.GetGenericArguments()
Dim TfromListOf As Type = listOfTParams(0)
Dim ienumOfT As Type = ienumOf.MakeGenericType(TfromListOf)
Dim ctorArgs() As Type = {ienumOfT}
Dim ctorPrep As ConstructorInfo = _
listOf.GetConstructor(ctorArgs)
Dim ctor As ConstructorInfo = _
TypeBuilder.GetConstructor(listOfTFirst, ctorPrep)
ilgen.Emit(OpCodes.Ldarg_0)
ilgen.Emit(OpCodes.Newobj, ctor)
ilgen.Emit(OpCodes.Ret)
' Create the type and save the assembly.
Dim finished As Type = myType.CreateType()
myAssembly.Save(myAsmName.Name & ".dll")
' Invoke the method.
' ExampleMethod is not generic, but the type it belongs to is
' generic, so in order to get a MethodInfo that can be invoked
' it is necessary to create a constructed type. The Example
' class satisfies the constraints on TFirst, because it is a
' reference type and has a default constructor. In order to
' have a class that satisfies the constraints on TSecond,
' this code example defines the ExampleDerived type. These
' two types are passed to MakeGenericMethod to create the
' constructed type.
'
Dim typeArgs() As Type = _
{GetType(Example), GetType(ExampleDerived)}
Dim constructed As Type = finished.MakeGenericType(typeArgs)
Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
' Create an array of Example objects, as input to the generic
' method. This array must be passed as the only element of an
' array of arguments. The first argument of Invoke is
' Nothing, because ExampleMethod is Shared. Display the count
' on the resulting List(Of Example).
'
Dim input() As Example = {New Example(), New Example()}
Dim arguments() As Object = {input}
Dim listX As List(Of Example) = mi.Invoke(Nothing, arguments)
Console.WriteLine(vbLf & _
"There are {0} elements in the List(Of Example).", _
listX.Count _
)
DisplayGenericParameters(finished)
End Sub
Private Shared Sub DisplayGenericParameters(ByVal t As Type)
If Not t.IsGenericType Then
Console.WriteLine("Type '{0}' is not generic.")
Return
End If
If Not t.IsGenericTypeDefinition Then _
t = t.GetGenericTypeDefinition()
Dim typeParameters() As Type = t.GetGenericArguments()
Console.WriteLine(vbCrLf & _
"Listing {0} type parameters for type '{1}'.", _
typeParameters.Length, t)
For Each tParam As Type In typeParameters
Console.WriteLine(vbCrLf & "Type parameter {0}:", _
tParam.ToString())
For Each c As Type In tParam.GetGenericParameterConstraints()
If c.IsInterface Then
Console.WriteLine(" Interface constraint: {0}", c)
Else
Console.WriteLine(" Base type constraint: {0}", c)
End If
Next
ListConstraintAttributes(tParam)
Next tParam
End Sub
' List the constraint flags. The GenericParameterAttributes
' enumeration contains two sets of attributes, variance and
' constraints. For this example, only constraints are used.
'
Private Shared Sub ListConstraintAttributes(ByVal t As Type)
' Mask off the constraint flags.
Dim constraints As GenericParameterAttributes = _
t.GenericParameterAttributes And _
GenericParameterAttributes.SpecialConstraintMask
If (constraints And GenericParameterAttributes.ReferenceTypeConstraint) _
<> GenericParameterAttributes.None Then _
Console.WriteLine(" ReferenceTypeConstraint")
If (constraints And GenericParameterAttributes.NotNullableValueTypeConstraint) _
<> GenericParameterAttributes.None Then _
Console.WriteLine(" NotNullableValueTypeConstraint")
If (constraints And GenericParameterAttributes.DefaultConstructorConstraint) _
<> GenericParameterAttributes.None Then _
Console.WriteLine(" DefaultConstructorConstraint")
End Sub
End Class
' This code example produces the following output:
'
'Type 'Sample' is generic: False
'Type 'Sample' is generic: True
'
'There are 2 elements in the List(Of Example).
'
'Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.
'
'Type parameter TFirst:
' ReferenceTypeConstraint
' DefaultConstructorConstraint
'
'Type parameter TSecond:
' Interface constraint: IExampleA
' Interface constraint: IExampleB
' Base type constraint: ExampleBase
こちらも参照ください
.NET