Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Önemli
Bu nasıl yapılır makalesi, modern .NET'te bulunmayan .NET Framework'e özgü API'leri gösterir. Dinamik derlemeyi modern .NET'te diske kaydetmek için türünü kullanın PersistedAssemblyBuilder .
Bu makalede şunların nasıl yapıldığını görebilirsiniz:
- İki tür parametresiyle basit bir genel tür oluşturun.
- Tür parametrelerine sınıf kısıtlamaları, arabirim kısıtlamaları ve özel kısıtlamalar uygulayın.
- Sınıfın tür parametrelerini parametre türleri ve dönüş türleri olarak kullanan üyeler oluşturun.
Önemli
Bir yöntem yalnızca genel bir türe ait olduğundan ve bu türün tür parametrelerini kullandığından genel değildir. Bir yöntem yalnızca kendi tür parametre listesi varsa geneldir. Genel türlerdeki yöntemlerin çoğu, bu örnekte olduğu gibi genel değildir. Genel bir yöntem oluşturma örneği için bkz Nasıl yapılır: Yansıma Emit ile Genel Yöntem Tanımlama.
Genel tür tanımlama
adlı
GenericEmitExample1bir dinamik derleme tanımlayın. Bu örnekte derleme yürütülür ve diske kaydedilir, bu nedenle AssemblyBuilderAccess.RunAndSave belirtilir.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)Dinamik bir modül tanımlayın. Derleme yürütülebilir modüllerden oluşur. Tek modüllü derleme için modül adı derleme adıyla aynıdır ve dosya adı modül adı ile bir uzantıdır.
ModuleBuilder myModule = myAssembly.DefineDynamicModule( myAsmName.Name, $"{myAsmName.Name}.dll");Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _ myAsmName.Name, _ myAsmName.Name & ".dll")Bir sınıf tanımlayın. Bu örnekte, sınıf
Sampleolarak adlandırılır.TypeBuilder myType = myModule.DefineType("Sample", TypeAttributes.Public);Dim myType As TypeBuilder = myModule.DefineType( _ "Sample", _ TypeAttributes.Public)Samplegenel tür parametrelerini, parametrelerin adlarını içeren bir dizi dizeyi TypeBuilder.DefineGenericParameters yöntemine geçirerek tanımlayın. Bu, sınıfı genel bir tür yapar. Dönüş değeri, yayılan kodunuzda kullanılabilecek tür parametrelerini temsil eden bir nesne dizisidir GenericTypeParameterBuilder .Aşağıdaki kodda,
Sampletür parametreleriTFirstveTSecondolan genel bir tür haline gelir. Kodun daha kolay okunmasını sağlamak için her GenericTypeParameterBuilder tür parametresiyle aynı ada sahip bir değişkene yerleştirilir.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)Tür parametrelerine özel kısıtlamalar ekleyin. Bu örnekte tür parametresi
TFirst, parametresiz oluşturucuları olan türlere ve başvuru türlerine kısıtlanır.TFirst.SetGenericParameterAttributes( GenericParameterAttributes.DefaultConstructorConstraint | GenericParameterAttributes.ReferenceTypeConstraint);TFirst.SetGenericParameterAttributes( _ GenericParameterAttributes.DefaultConstructorConstraint _ Or GenericParameterAttributes.ReferenceTypeConstraint)İsteğe bağlı olarak tür parametrelerine sınıf ve arabirim kısıtlamaları ekleyin. Bu örnekte, tür parametresi
TFirst, değişken Type içinde bulunanbaseTypenesnesi tarafından temsil edilen temel sınıftan türetilen veinterfaceAileinterfaceBdeğişkenlerinde bulunan arabirimleri uygulayan türlerle kısıtlanır. Bu değişkenlerin bildirimi ve ataması için kod örneğine bakın.TSecond.SetBaseTypeConstraint(baseType); Type[] interfaceTypes = { interfaceA, interfaceB }; TSecond.SetInterfaceConstraints(interfaceTypes);TSecond.SetBaseTypeConstraint(baseType) Dim interfaceTypes() As Type = {interfaceA, interfaceB} TSecond.SetInterfaceConstraints(interfaceTypes)Alan tanımlayın. Bu örnekte, alanın türü parametresine
TFirstgöre belirtilir. GenericTypeParameterBuilder türünden Typetüretilir, böylece bir türün kullanabildiği her yerde genel tür parametrelerini kullanabilirsiniz._ = myType.DefineField("ExampleField", TFirst, FieldAttributes.Private);Dim exField As FieldBuilder = _ myType.DefineField("ExampleField", TFirst, _ FieldAttributes.Private)Genel türün tür parametrelerini kullanan bir yöntem tanımlayın. Bu tür yöntemlerin kendi tür parametre listeleri olmadığı sürece genel olmadığını unutmayın. Aşağıdaki kod, bir
staticdizisini alıp, dizinin tüm öğelerini içeren birShared(TFirstVisual Basic'te) döndüren birList<TFirst>yöntemi (List(Of TFirst)Visual Basic'te) tanımlar. Bu yöntemi tanımlamak için, genel tür tanımınaList<TFirst>çağrısı yaparak MakeGenericType türünüList<T>ile oluşturmak gerekir. (T,typeofişlecini (GetTypeVisual Basic'te) genel tür tanımını almak için kullandığınızda atlanır.) Parametre türü yöntemi kullanılarak MakeArrayType oluşturulur.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)Yöntem gövdesini oluşturur. Yöntem gövdesi, giriş dizisini yığına yükleyen ve
List<TFirst>alanIEnumerable<TFirst>oluşturucuyu çağırarak (giriş öğelerini listeye eklemenin tüm işini yapan) döndüren (yeni List<T> nesneyi yığında bırakan) üç işlem kodundan oluşur. Bu kodu yayımlamanın zor kısmı, oluşturucuyu elde etmektir.GetConstructor yöntemi GenericTypeParameterBuilder üzerinde desteklenmez, bu nedenle
List<TFirst>oluşturucuyu doğrudan almak mümkün değildir. İlk olarak, genel tür tanımınınList<T>oluşturucusunu almak ve ardından bunuList<TFirst>öğesine karşılık gelen oluşturucuya dönüştüren bir yöntemi çağırmak gerekir.Bu kod örneği için kullanılan oluşturucu bir
IEnumerable<T>alır. Ancak, bunun IEnumerable<T> genel arabiriminin genel tip tanımı olmadığını unutmayın; bunun yerine,Tiçindeki tür parametresiList<T>ileTiçindeki tür parametresiIEnumerable<T>değiştirilmelidir. (Bu, her iki türün deTadında tür parametrelerine sahip olmasından dolayı kafa karıştırıcı görünebilir. Bu nedenle, bu kod örneğiTFirstveTSecondadlarını kullanır.) Oluşturucu bağımsız değişkeninin türünü elde etmek için, genel tür tanımıIEnumerable<T>ile başlayın ve MakeGenericType'in ilk genel tür parametresiyleList<T>'ı çağırın. Oluşturucu bağımsız değişken listesi, bu durumda sadece bir bağımsız değişken içeren bir dizi olarak geçirilmelidir.Uyarı
Genel tür tanımı, C# dilinde
IEnumerable<>işleci kullanıldığındatypeofolarak ya da Visual Basic dilindeIEnumerable(Of )işleci kullanıldığındaGetTypeolarak ifade edilir.Artık genel tür tanımında
List<T>çağrısı yaparak GetConstructor oluşturucu almak mümkündür. Bu oluşturucuyuList<TFirst>'nun karşılık gelen oluşturucusuna dönüştürmek için, bu işlem için,List<TFirst>'danList<T>ve oluşturucuyu, statik TypeBuilder.GetConstructor(Type, ConstructorInfo) yöntemine gönderin.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)Türünü oluşturun ve dosyayı kaydedin.
Type finished = myType.CreateType(); myAssembly.Save(myAsmName.Name + ".dll");Dim finished As Type = myType.CreateType() myAssembly.Save(myAsmName.Name & ".dll")yöntemini çağırın.
ExampleMethodgenel değildir, ancak ait olduğu tür geneldir, bu nedenle çağrılabilen bir MethodInfo almak için, içinSampletür tanımından yapılandırılmış bir tür oluşturmak gerekir. Oluşturulmuş tür, bir başvuru türü olduğu ve varsayılan parametresiz bir oluşturucuya sahip olduğu içinExampleüzerindeki kısıtlamaları karşılayanTFirstsınıfını veExampleDerivedüzerindeki kısıtlamaları karşılayanTSecondsınıfını kullanır. (ExampleDerivediçin kod, örnek kod bölümünde bulunabilir.) Bu iki tür, oluşturulmuş türü yaratmak için MakeGenericType'ye geçirilir. MethodInfo, GetMethod yöntemi kullanılarak daha sonra elde edilir.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")Aşağıdaki kod bir nesne dizisi
Exampleoluşturur, bu diziyi çağrılacak yöntemin bağımsız değişkenlerini temsil eden bir tür Object dizisine yerleştirir ve bunları yöntemine Invoke(Object, Object[]) geçirir. Invoke yönteminin ilk bağımsız değişkeni bir null başvurudur çünkü yöntemstaticşeklindedir.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 _ )
Örnek
Aşağıdaki kod örneği, programın tamamını gösterir. adlı Samplebir sınıfı, bir temel sınıf ve iki arabirimle birlikte tanımlar. Program için Sampleiki genel tür parametresi tanımlar ve bunu genel bir türe dönüştürür. Tür parametreleri, türü genel yapan tek şeydir. Program bunu, tür parametrelerinin tanımından önce ve sonra bir test iletisi görüntüleyerek gösterir.
tür parametresi TSecond , temel sınıf ve arabirimler kullanılarak sınıf ve arabirim kısıtlamalarını göstermek için kullanılır ve tür parametresi TFirst özel kısıtlamaları göstermek için kullanılır.
Kod örneği, alan türü ve yönteminin parametresi ve dönüş türü için sınıfın tür parametrelerini kullanan bir alan ve yöntem tanımlar.
Sample sınıf oluşturulduktan sonra yöntemi çağrılır.
Program, genel bir tür hakkındaki bilgileri listeleyen bir yöntem ve tür parametresindeki özel kısıtlamaları listeleyen bir yöntem içerir. Bu yöntemler, tamamlanmış Sample sınıf hakkındaki bilgileri görüntülemek için kullanılır.
Program, tamamlanmış modülü olarak GenericEmitExample1.dlldiske kaydeder, böylece Ildasm.exe (IL Disassembler) ile açabilir ve sınıfın CIL'sini Sample inceleyebilirsiniz.
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