كيفية القيام بما يلي: قم بتعريف نوع عام مع انعكاس حذف
يوضح هذا الموضوع كيفية إنشاء نوع عام بسيط مع نوع معلمتين وكيفية يطبق قيود فئة واجهة القيود والقيود الخاصة بنوع المعلمات وكيفية إنشاء الأعضاء التي تستخدم المعلمات نوع الفئة كمعلمة أنواع والعودة أنواع.
![]() |
---|
أسلوب هو العامة ليس فقط لأنه ينتمي إلى نوع عام ويستخدم معلمات نوع من هذا النوع.أسلوب هو عام فقط إذا كان لديه lهوt معلمة النوع الخاص به.معظم وظائف تشغيل الأنواع العامة غير عام، كما في ذلك مثال.على سبيل مثال من باعث أسلوب عام، راجع كيفية القيام بما يلي: قم بتعريف أسلوب عام مع انعكاس حذف. |
لتعريف نوع عام
تعرف ديناميكي تجميع المسمى GenericEmitExample1. في ترتيب هو سبيل المثال، تجميع هو تنفيذها وحفظها dهوk، في هذه الحالة AssemblyBuilderAccess.RunAndSaveهو المحدد.
Dim myDomain As AppDomain = AppDomain.CurrentDomain Dim myAsmName As New AssemblyName("GenericEmitExample1") Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _ myAsmName, _ AssemblyBuilderAccess.RunAndSave)
AppDomain myDomain = AppDomain.CurrentDomain; AssemblyName myAsmName = new AssemblyName("GenericEmitExample1"); AssemblyBuilder myAssembly = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
AppDomain^ myDomain = AppDomain::CurrentDomain; AssemblyName^ myAsmName = gcnew AssemblyName( L"GenericEmitExample1" ); AssemblyBuilder^ myAssembly = myDomain->DefineDynamicAssembly( myAsmName, AssemblyBuilderAccess::RunAndSave );
قم بتحديد وحدة نمطية حيوية. تجميع يتكون من الوحدات النمطية القابلة للتنفيذ. بالنسبة للوحدة النمطية لواحد كـ sembly، هو اسم وحدة نمطية? نفسها كـ اسم كـ sembly، واسم الملف هو اسم وحدة نمطية? بالإضافة إلى ملحق.
Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _ myAsmName.Name, _ myAsmName.Name & ".dll")
ModuleBuilder myModule = myAssembly.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
ModuleBuilder^ myModule = myAssembly->DefineDynamicModule( myAsmName->Name, String::Concat( myAsmName->Name, L".dll" ) );
قم بتحديد فئة. في ترتيب هو سبيل المثال، الفئة هو يسمى Sample.
Dim myType As TypeBuilder = myModule.DefineType( _ "Sample", _ TypeAttributes.Public)
TypeBuilder myType = myModule.DefineType("Sample", TypeAttributes.Public);
TypeBuilder^ myType = myModule->DefineType( L"Sample", TypeAttributes::Public );
تعريف معلمة نوع عام من Sampleبتمرير صفيفة سلاسل التي تحتوي على أسماء معلمات إلى TypeBuilder.DefineGenericParametersالأسلوب. وهذا يجعل الفئة نوع عام. القيمة الإرجاع هو صفيف GenericTypeParameterBuilderالكائنات التي تمثل معلمات نوع، والتي يمكن استخدامها في الخاص بك رمز emitted.
في ما يلي تعليمات برمجية، Sampleيصبح نوع عام مع معلمات نوع TFirstو TSecond. لتسهيل قراءة كل التعليمة البرمجية GenericTypeParameterBuilderهو وضعها في متغير يحمل نفس الاسم كمعلمة نوع.
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)
string[] typeParamNames = {"TFirst", "TSecond"}; GenericTypeParameterBuilder[] typeParams = myType.DefineGenericParameters(typeParamNames); GenericTypeParameterBuilder TFirst = typeParams[0]; GenericTypeParameterBuilder TSecond = typeParams[1];
array<String^>^typeParamNames = {L"TFirst",L"TSecond"}; array<GenericTypeParameterBuilder^>^typeParams = myType->DefineGenericParameters( typeParamNames ); GenericTypeParameterBuilder^ TFirst = typeParams[0]; GenericTypeParameterBuilder^ TSecond = typeParams[1];
إضافة قيود خاص إلى نوع المعلمات. في هذا المثال، اكتب معلمة TFirstكانت مقيدة ب أنواع التي تحتوي المنشئات بدون معلمات، ومرجع أنواع.
TFirst.SetGenericParameterAttributes( _ GenericParameterAttributes.DefaultConstructorConstraint _ Or GenericParameterAttributes.ReferenceTypeConstraint)
TFirst.SetGenericParameterAttributes( GenericParameterAttributes.DefaultConstructorConstraint | GenericParameterAttributes.ReferenceTypeConstraint);
TFirst->SetGenericParameterAttributes( GenericParameterAttributes::DefaultConstructorConstraint | GenericParameterAttributes::ReferenceTypeConstraint );
بشكل اختياري إضافة قيود الفئة وواجهة إلى نوع المعلمات. في هذا المثال، اكتب معلمة TFirstكانت مقيدة ب أنواع الذي ينحدر من فئة Base الممثلة Typeالكائنات الموجودة في المتغير baseType، والتي تطبق الواجهات الخاصة به أنواع مضمنة في المتغيرات interfaceAو interfaceB. راجع المثال تعليمات برمجية للتعريف و التعيين الخاص بهذه المتغيرات.
TSecond.SetBaseTypeConstraint(baseType) Dim interfaceTypes() As Type = {interfaceA, interfaceB} TSecond.SetInterfaceConstraints(interfaceTypes)
TSecond.SetBaseTypeConstraint(baseType); Type[] interfaceTypes = {interfaceA, interfaceB}; TSecond.SetInterfaceConstraints(interfaceTypes);
array<Type^>^interfaceTypes = { interfaceA, interfaceB }; TSecond->SetInterfaceConstraints( interfaceTypes ); TSecond->SetBaseTypeConstraint( baseType );
قم بتعريف حقل. في ترتيب هو سبيل المثال، نوع الحقل هو المحدد بواسطة معلمة نوع TFirst. GenericTypeParameterBuilderمشتقة منType، لذلك يمكنك استخدم معلمات نوع عام في أي مكان يمكن أن يكون نوع استخدم d.
Dim exField As FieldBuilder = _ myType.DefineField("ExampleField", TFirst, _ FieldAttributes.Private)
FieldBuilder exField = myType.DefineField("ExampleField", TFirst, FieldAttributes.Private);
FieldBuilder^ exField = myType->DefineField("ExampleField", TFirst, FieldAttributes::Private);
قم بتعريف أسلوب يستخدم معلمات النوع نوع عام. لاحظ أن هذه الأساليب غير عام إلا إذا كان لديها الخاصة بهم نوع معلمة سرد. ما يلي تعليمات برمجية يعرف staticالأسلوب ( Sharedفي Visual أساسى) التي تأخذ صفيفة من TFirstوإرجاع List<TFirst>(List(Of TFirst)in Visual أساسى) يحتوي على الجميع العناصر الموجودة في صفيفة. لتعريف th هو الأسلوب، فإنه هو الضرورية لإنشاء نوع List<TFirst>بواسطة استدعاء MakeGenericTypeفي تعريف نوع عام، List<T>. ( Tهو تم حذفها عند استخدام typeofعامل التشغيل ( GetTypeفي Vهوual أساسي) للحصول على تعريف نوع عام.) نوع معلمة هو التي تم إنشاؤها باستخدام MakeArrayTypeالأسلوب.
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)
Type listOf = typeof(List<>); Type listOfTFirst = listOf.MakeGenericType(TFirst); Type[] mParamTypes = {TFirst.MakeArrayType()}; MethodBuilder exMethod = myType.DefineMethod("ExampleMethod", MethodAttributes.Public | MethodAttributes.Static, listOfTFirst, mParamTypes);
Type^ listOf = List::typeid; Type^ listOfTFirst = listOf->MakeGenericType(TFirst); array<Type^>^ mParamTypes = { TFirst->MakeArrayType() }; MethodBuilder^ exMethod = myType->DefineMethod("ExampleMethod", MethodAttributes::Public | MethodAttributes::Static, listOfTFirst, mParamTypes);
الإرسال أسلوب نص الرسالة. نص الأسلوب يتكون من ثلاثة العمليات التي تقوم بتحميل صفيفة إدخال إلى المكدس، قم باستدعاء List<TFirst>الدالة الإنشائية التي تأخذ IEnumerable<TFirst>(which does الجميع the work of putting the إدخال عناصر into the قائمة) ، وإرجاع (ترك الجديدة List<T>الكائن تشغيل المكدس). يتم الحصول على الجزء الصعب من باعث هذه التعليمة البرمجية الدالة الإنشائية.
GetConstructorالأسلوب غير معتمد تشغيل GenericTypeParameterBuilder، لذلك ليس من الممكن الحصول تشغيل الدالة الإنشائية من List<TFirst>مباشرة. أول، فإنه هو اللازمة للحصول على الدالة الإنشائية عام كتابة تعريف List<T>، ثم القيام باستدعاء أسلوب الذي يحول إلى الدالة الإنشائية المطابق من List<TFirst>.
الدالة الإنشائية المستخدم لهذه التعليمة البرمجية مثال يأخذ IEnumerable<T>. ملاحظة، على الرغم من ذلك، هذا th هو هو لا في تعريف النوع العام من IEnumerable<T>واجهة عامة؛ وبدلاً من ذلك، معلمة نوع Tمن List<T>يجب أن يتم استبداله للمعلمة نوع Tمن IEnumerable<T>. (وهذا يبدو مربكاً فقط لأن كلا النوعين بوجود معلمات نوع يسمى T. that هو لماذا يستخدم هذا المثال تعليمات برمجية أسماء TFirstو TSecond.) للحصول على النوع الدالة الإنشائية وسيطة، تبدأ ب نوع عام تعريف IEnumerable<T>واستدعاء MakeGenericTypeمع معلمة نوع عام الأولى من List<T>. يجب أن تكون قائمة وسائط الدالة الإنشائية pكـsed كـ صفيف باستخدام وسيطة واحدة فقط في هذا cكـe.
ملاحظة
تعريف النوع العام هو يعبر IEnumerable<>عند استخدام typeofعامل في C#, أو IEnumerable(Of )عند استخدام GetTypeعامل في أساسى Vهوual.
الآن من الممكن الحصول تشغيل المنشئ List<T>بواسطة استدعاء GetConstructorفي تعريف نوع عام. إلى تحويل this الدالة الإنشائية إلى the corresponding الدالة الإنشائية of List<TFirst>, pass List<TFirst> و the الدالة الإنشائية من List<T> إلى the ثابت TypeBuilder.GetConstructor(Type, ConstructorInfo) أسلوب.
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)
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);
ILGenerator^ ilgen = exMethod->GetILGenerator(); Type^ ienumOf = IEnumerable::typeid; Type^ TfromListOf = listOf->GetGenericArguments()[0]; Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf); array<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);
إنشاء the نوع و حفظ the ملف.
Dim finished As Type = myType.CreateType() myAssembly.Save(myAsmName.Name & ".dll")
Type finished = myType.CreateType(); myAssembly.Save(myAsmName.Name+".dll");
Type^ finished = myType->CreateType(); myAssembly->Save( String::Concat( myAsmName->Name, L".dll" ) );
Invoke the أسلوب. ExampleMethod ليس generic, but the نوع it belongs إلى هو generic, so في ترتيب إلى يحصل a MethodInfo that can be invoked it هو necessary إلى إنشاء a constructed نوع من the نوع تعريف for Sample. يستخدم نوع constructed Exampleالفئة التي satهوfies القيود الموجودة تشغيل TFirstلأن ذلك هو نوع مرجع وله الدالة الإنشائية بدون معلمات افتراضية، و ExampleDerivedللفئات التي satهوfies القيود تشغيل TSecond. (تعليمات برمجية ل ExampleDerivedفي المثال تعليمات برمجية مقطع.) انتشار هذه أنواع التي تم تمريرها إلى MakeGenericTypeلإنشاء نوع constructed. MethodInfoهو ثم الحصول عليها باستخدام GetMethodالأسلوب.
Dim typeArgs() As Type = _ { GetType(Example), GetType(ExampleDerived) } Dim constructed As Type = finished.MakeGenericType(typeArgs) Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)}; Type constructed = finished.MakeGenericType(typeArgs); MethodInfo mi = constructed.GetMethod("ExampleMethod");
array<Type^>^ typeArgs = { Example::typeid, ExampleDerived::typeid }; Type^ constructed = finished->MakeGenericType(typeArgs); MethodInfo^ mi = constructed->GetMethod("ExampleMethod");
ما يلي تعليمات برمجية إنشاء صفيفة من Exampleالكائنات، قم بوضع هذا الصفيفة في صفيفة نوع Objectتمثل وسائط الأسلوب الذي يتم طلبه، وينقلها إلى Invoke(Object, array<Object[])الأسلوب. وسيطة الأولى من Invokeأسلوب لأنه مرجع فارغ أسلوب هو static.
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 _ )
Example[] input = {new Example(), new Example()}; object[] arguments = {input}; List<Example> listX = (List<Example>) mi.Invoke(null, arguments); Console.WriteLine( "\nThere are {0} elements in the List<Example>.", listX.Count);
array<Example^>^ input = { gcnew Example(), gcnew Example() }; array<Object^>^ arguments = { input }; List<Example^>^ listX = (List<Example^>^) mi->Invoke(nullptr, arguments); Console::WriteLine( "\nThere are {0} elements in the List<Example>.", listX->Count);
مثال
يلي تعليمات برمجية مثال تعريف فئة باسم Sample، مع فئة أساسية وواجهات الثاني. يحدد البرنامج الثاني معلمات نوع عامة Sample، إيقاف تشغيله في نوع عام. معلمات نوع هي الشيء الوحيد الذي يجعل نوعا عاماً. يعرض البرنامج بهذا بواسطة عرض رسالة اختبارية قبل و بعد تعريف معلمة نوع.
نوع معلمة TSecondيستخدم لتوضيح القيود الفئة والواجهة، باستخدام فئة Base والواجهات، ونوع معلمة TFirstيستخدم لتوضيح خاص القيود.
تعليمات برمجية المثال بتعريف حقل وأسلوب استخدام معلمة نوع للفئة لنوع الحقل ونوع المعلمة والإرجاع للأسلوب.
بعد Sampleفئة تم إنشاؤه، الأسلوب هو استدعاء.
يتضمن البرنامج أسلوب الذي يسرد معلومات حول نوع عام، و أسلوب يقوم بسرد القيود الخاصة تشغيل معلمة نوع. يتم استخدام هذه الطرق إلى عرض معلومات حول انتهاء Sampleفئة.
البرنامج بحفظ وحدة نمطية? للانتهاء من إلى قرص GenericEmitExample1.dll، لذلك يمكنك فتحه مع Ildasm.exe (المفكك MSIL)MSIL للتحقق Sampleفئة.
Imports System
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
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: {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.
//
string[] typeParamNames = {"TFirst", "TSecond"};
GenericTypeParameterBuilder[] typeParams =
myType.DefineGenericParameters(typeParamNames);
GenericTypeParameterBuilder TFirst = typeParams[0];
GenericTypeParameterBuilder TSecond = 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 |
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 {0} elements in the List<Example>.",
listX.Count);
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 {0} type parameters for type '{1}'.",
typeParameters.Length, t);
foreach( Type tParam in typeParameters )
{
Console.WriteLine("\r\nType parameter {0}:", tParam.ToString());
foreach( Type c in tParam.GetGenericParameterConstraints() )
{
if (c.IsInterface)
{
Console.WriteLine(" Interface constraint: {0}", c);
}
else
{
Console.WriteLine(" Base type constraint: {0}", 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
*/
using namespace System;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
using namespace System::Collections::Generic;
// Dummy class to satisfy TFirst constraints.
//
public ref class Example {};
// Define a trivial base class and two trivial interfaces
// to use when demonstrating constraints.
//
public ref class ExampleBase {};
public interface class IExampleA {};
public interface class IExampleB {};
// Define a trivial type that can substitute for type parameter
// TSecond.
//
public ref class ExampleDerived : ExampleBase, IExampleA, IExampleB {};
// List the constraint flags. The GenericParameterAttributes
// enumeration contains two sets of attributes, variance and
// constraints. For this example, only constraints are used.
//
static void ListConstraintAttributes( Type^ t )
{
// Mask off the constraint flags.
GenericParameterAttributes constraints =
t->GenericParameterAttributes &
GenericParameterAttributes::SpecialConstraintMask;
if ((constraints & GenericParameterAttributes::ReferenceTypeConstraint)
!= GenericParameterAttributes::None)
Console::WriteLine( L" ReferenceTypeConstraint");
if ((constraints & GenericParameterAttributes::NotNullableValueTypeConstraint)
!= GenericParameterAttributes::None)
Console::WriteLine( L" NotNullableValueTypeConstraint");
if ((constraints & GenericParameterAttributes::DefaultConstructorConstraint)
!= GenericParameterAttributes::None)
Console::WriteLine( L" DefaultConstructorConstraint");
}
static void DisplayGenericParameters( Type^ t )
{
if (!t->IsGenericType)
{
Console::WriteLine( L"Type '{0}' is not generic." );
return;
}
if (!t->IsGenericTypeDefinition)
t = t->GetGenericTypeDefinition();
array<Type^>^ typeParameters = t->GetGenericArguments();
Console::WriteLine( L"\r\nListing {0} type parameters for type '{1}'.",
typeParameters->Length, t );
for each ( Type^ tParam in typeParameters )
{
Console::WriteLine( L"\r\nType parameter {0}:",
tParam->ToString() );
for each (Type^ c in tParam->GetGenericParameterConstraints())
{
if (c->IsInterface)
Console::WriteLine( L" Interface constraint: {0}", c);
else
Console::WriteLine( L" Base type constraint: {0}", c);
}
ListConstraintAttributes(tParam);
}
}
void main()
{
// Define a dynamic assembly to contain the sample type. The
// assembly will be run and also saved to disk, so
// AssemblyBuilderAccess.RunAndSave is specified.
//
AppDomain^ myDomain = AppDomain::CurrentDomain;
AssemblyName^ myAsmName = gcnew AssemblyName( L"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, String::Concat( myAsmName->Name, L".dll" ) );
// Get type objects for the base class trivial interfaces to
// be used as constraints.
//
Type^ baseType = ExampleBase::typeid;
Type^ interfaceA = IExampleA::typeid;
Type^ interfaceB = IExampleB::typeid;
// Define the sample type.
//
TypeBuilder^ myType = myModule->DefineType( L"Sample",
TypeAttributes::Public );
Console::WriteLine( L"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.
//
array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
array<GenericTypeParameterBuilder^>^typeParams =
myType->DefineGenericParameters( typeParamNames );
GenericTypeParameterBuilder^ TFirst = typeParams[0];
GenericTypeParameterBuilder^ TSecond = typeParams[1];
Console::WriteLine( L"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 |
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.
array<Type^>^interfaceTypes = { interfaceA, interfaceB };
TSecond->SetInterfaceConstraints( interfaceTypes );
TSecond->SetBaseTypeConstraint( baseType );
// 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, generic<T> List.
// The parameter type is created by using the
// MakeArrayType method.
//
Type^ listOf = List::typeid;
Type^ listOfTFirst = listOf->MakeGenericType(TFirst);
array<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 generic<T> List 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 generic<T> IEnumerable; instead, the
// T from generic<T> List must be substituted for the T of
// generic<T> IEnumerable. (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 generic<T> IEnumerable and
// call MakeGenericType with the first generic type parameter
// of generic<T> List. 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 generic<T> List,
// using GetConstructor on the generic type definition. To get
// the constructor of List<TFirst>, pass List<TFirst> and
// the constructor from generic<T> List to the static
// TypeBuilder.GetConstructor method.
//
ILGenerator^ ilgen = exMethod->GetILGenerator();
Type^ ienumOf = IEnumerable::typeid;
Type^ TfromListOf = listOf->GetGenericArguments()[0];
Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf);
array<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( String::Concat( myAsmName->Name, L".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.
//
array<Type^>^ typeArgs =
{ Example::typeid, ExampleDerived::typeid };
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>.
//
array<Example^>^ input = { gcnew Example(), gcnew Example() };
array<Object^>^ arguments = { input };
List<Example^>^ listX =
(List<Example^>^) mi->Invoke(nullptr, arguments);
Console::WriteLine(
"\nThere are {0} elements in the List<Example>.",
listX->Count);
DisplayGenericParameters(finished);
}
/* 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
*/
التحويل البرمجي للتعليمات البرمجية
The تعليمات برمجية يحتوي على the C# using statements (Imports في Visual أساسى) necessary for compilation.
لا توجد مراجع تجميع إضافى مطلوبة.
يحول برمجياً the تعليمات برمجية at the الأمر خط using csc.exe, vbc.exe, أو cl.exe. إلى يحول برمجياً the تعليمات برمجية في Visual Studio, place it في a تطبيق وحدة تحكم قالب مشروع.
راجع أيضًا:
المرجع
المبادئ
انعكاس الإرسال سيناريوهات تجميع حيوي