Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Viktigt!
Den här artikeln visar .NET Framework-specifika API:er som inte är tillgängliga i moderna .NET. Om du vill spara en dynamisk sammansättning på disk i modern .NET använder du typen PersistedAssemblyBuilder .
Den här artikeln visar hur du gör följande:
- Skapa en enkel allmän typ med två typparametrar.
- Tillämpa klassbegränsningar, gränssnittsbegränsningar och särskilda begränsningar på typparametrarna.
- Skapa medlemmar som använder typparametrarna för klassen som parametertyper och returtyper.
Viktigt!
En metod är inte generisk bara för att den tillhör en allmän typ och använder typparametrarna av den typen. En metod är endast generisk om den har en egen typparameterlista. De flesta metoder för generiska typer är inte generiska, som i det här exemplet. Ett exempel på hur du genererar en generisk metod finns i How to: Define a Generic Method with Reflection Emit.
Definiera en allmän typ
Definiera en dynamisk sammansättning med namnet
GenericEmitExample1. I det här exemplet körs och sparas sammansättningen på disken, så AssemblyBuilderAccess.RunAndSave anges.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)Definiera en dynamisk modul. En sammansättning består av körbara moduler. För en sammansättning med en modul är modulnamnet samma som sammansättningsnamnet och filnamnet är modulnamnet plus ett tillägg.
ModuleBuilder myModule = myAssembly.DefineDynamicModule( myAsmName.Name, $"{myAsmName.Name}.dll");Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _ myAsmName.Name, _ myAsmName.Name & ".dll")Definiera en klass. I det här exemplet heter klassen
Sample.TypeBuilder myType = myModule.DefineType("Sample", TypeAttributes.Public);Dim myType As TypeBuilder = myModule.DefineType( _ "Sample", _ TypeAttributes.Public)Definiera de allmänna typparametrarna för
Samplegenom att skicka en matris med strängar som innehåller namnen på parametrarna till metoden TypeBuilder.DefineGenericParameters. Det gör klassen till en allmän typ. Returvärdet är en matris med GenericTypeParameterBuilder objekt som representerar typparametrarna, som kan användas i koden som genereras.I följande kod blir
Sampleen allmän typ med typparametrarTFirstochTSecond. För att göra koden enklare att läsa placeras varje GenericTypeParameterBuilder i en variabel med samma namn som typparametern.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)Lägg till särskilda begränsningar i typparametrarna. I det här exemplet är typparametern
TFirstbegränsad till typer som har parameterlösa konstruktorer och referenstyper.TFirst.SetGenericParameterAttributes( GenericParameterAttributes.DefaultConstructorConstraint | GenericParameterAttributes.ReferenceTypeConstraint);TFirst.SetGenericParameterAttributes( _ GenericParameterAttributes.DefaultConstructorConstraint _ Or GenericParameterAttributes.ReferenceTypeConstraint)Du kan också lägga till klass- och gränssnittsbegränsningar i typparametrarna. I det här exemplet är typparametern
TFirstbegränsad till typer som härleds från basklassen som representeras av Type-objektet i variabelnbaseTypeoch som implementerar de gränssnitt vars typer finns i variablernainterfaceAochinterfaceB. Se kodexemplet för deklaration och tilldelning av dessa variabler.TSecond.SetBaseTypeConstraint(baseType); Type[] interfaceTypes = { interfaceA, interfaceB }; TSecond.SetInterfaceConstraints(interfaceTypes);TSecond.SetBaseTypeConstraint(baseType) Dim interfaceTypes() As Type = {interfaceA, interfaceB} TSecond.SetInterfaceConstraints(interfaceTypes)Definiera ett fält. I det här exemplet anges typen av fältet efter typparameter
TFirst. GenericTypeParameterBuilder härleds från Type, så att du kan använda allmänna typparametrar var som helst där en typ kan användas._ = myType.DefineField("ExampleField", TFirst, FieldAttributes.Private);Dim exField As FieldBuilder = _ myType.DefineField("ExampleField", TFirst, _ FieldAttributes.Private)Definiera en metod som använder typparametrarna för den generiska typen. Observera att sådana metoder inte är generiska om de inte har egna typparameterlistor. Följande kod definierar en
static-metod (Sharedi Visual Basic) som tar en matris medTFirstoch returnerar enList<TFirst>(List(Of TFirst)i Visual Basic) som innehåller alla element i matrisen. För att definiera den här metoden måste du skapa typenList<TFirst>genom att anropa MakeGenericType på den generiska typdefinitionenList<T>. (Tutelämnas när du använder operatorntypeof(GetTypei Visual Basic) för att hämta den allmänna typdefinitionen.) Parametertypen skapas med hjälp av metoden 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)Generera metodkroppen. Metodkroppen består av tre opcodes som laddar inmatningsmatrisen till stacken, anropar
List<TFirst>-konstruktorn som tarIEnumerable<TFirst>(vilket gör allt arbete med att placera elementen från inmatningen i listan) och returnerar (lämnar det nya List<T>-objektet på stacken). Den svåra delen av att skapa den här koden är att få tag på konstruktorn.Metoden GetConstructor stöds inte på en GenericTypeParameterBuilder, så det går inte att hämta konstruktorn för
List<TFirst>direkt. Först är det nödvändigt att hämta konstruktorn för den generiska typdefinitionenList<T>och sedan anropa en metod som konverterar den till motsvarande konstruktor förList<TFirst>.Konstruktorn som används i det här kodexemplet accepterar en
IEnumerable<T>. Observera dock att detta inte är den allmänna typdefinitionen av det IEnumerable<T> generiska gränssnittet. I stället måste typparameternTfrånList<T>ersättas med typparameternTförIEnumerable<T>. (Detta verkar förvirrande bara eftersom båda typerna har typparametrar med namnetT. Därför använder det här kodexemplet namnenTFirstochTSecond.) Om du vill hämta typen av konstruktorargument börjar du med den generiska typdefinitionenIEnumerable<T>och anropar MakeGenericType med den första generiska typparametern förList<T>. Konstruktorns argumentlista måste skickas som en matris, med bara ett argument i det här fallet.Anmärkning
Den allmänna typdefinitionen uttrycks som
IEnumerable<>när du använder operatorntypeofi C#, ellerIEnumerable(Of )när du använder operatornGetTypei Visual Basic.Nu är det möjligt att hämta konstruktorn för
List<T>genom att anropa GetConstructor på den generiska typdefinitionen. Om du vill konvertera konstruktorn till motsvarande konstruktor förList<TFirst>skickar duList<TFirst>och konstruktorn frånList<T>till metoden static 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)Skapa typen och spara filen.
Type finished = myType.CreateType(); myAssembly.Save(myAsmName.Name + ".dll");Dim finished As Type = myType.CreateType() myAssembly.Save(myAsmName.Name & ".dll")Anropa metoden.
ExampleMethodär inte generisk, men typen den tillhör är generisk, så för att få en MethodInfo som kan anropas är det nödvändigt att skapa en konstruerad typ från typdefinitionen förSample. Den konstruerade typen använder klassenExample, som uppfyller begränsningarna förTFirsteftersom den är en referenstyp och har en standardkonstruktor utan parameter och klassenExampleDerivedsom uppfyller begränsningarna förTSecond. (Koden förExampleDerivedfinns i exempelkodavsnittet.) Dessa två typer skickas till MakeGenericType för att skapa den konstruerade typen. MethodInfo hämtas sedan med hjälp av metoden 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")Följande kod skapar en matris med
Exampleobjekt, placerar matrisen i en matris av typen Object som representerar argumenten för den metod som ska anropas och skickar dem till metoden Invoke(Object, Object[]). Det första argumentet för metoden Invoke är en null-referens eftersom metoden ärstatic.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 _ )
Exempel
I följande kodexempel visas hela programmet. Den definierar en klass med namnet Sample, tillsammans med en basklass och två gränssnitt. Programmet definierar två generiska typparametrar för Sample, vilket gör det till en allmän typ. Typparametrar är det enda som gör en typ generisk. Programmet visar detta genom att visa ett testmeddelande före och efter definitionen av typparametrarna.
Typparametern TSecond används för att demonstrera klass- och gränssnittsbegränsningar, med hjälp av basklassen och gränssnitten, och typparametern TFirst används för att demonstrera särskilda begränsningar.
Kodexemplet definierar ett fält och en metod med hjälp av klassens typparametrar för fälttypen och för parametern och returtypen för metoden.
När klassen Sample har skapats anropas metoden.
Programmet innehåller en metod som visar information om en allmän typ och en metod som visar de särskilda begränsningarna för en typparameter. Dessa metoder används för att visa information om den färdiga Sample-klassen.
Programmet sparar den färdiga modulen på disken som GenericEmitExample1.dll, så att du kan öppna den med Ildasm.exe (IL Disassembler) och undersöka CIL för klassen Sample.
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 won't be run, 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.
_ = 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's 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's not possible to get
// the constructor of List<TFirst> directly. There are two
// steps: 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's 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($"\nType parameter {tParam}:");
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