مشاركة عبر


كيفية القيام بما يلي: قم بتعريف أسلوب عام مع انعكاس حذف

هو موضح في إجراء أول كيف إلى إنشاء أسلوب عام بسيط مع نوع معلمتين، وكيف إلى يطبق قيود فئة واجهة قيود وقيود خاص إلى نوع المعلمات.

يوضح إجراء الثاني كيفية إرسال نص الأسلوب، وكيفية استخدام معلمات نوع الأسلوب العام لإنشاء مثيلات لعام أنواع واستدعاء الأساليب الخاصة بهم.

هو موضح في إجراء الثالث كيفية إلى استدعاء الأسلوب العام.

ملاحظة هامةهام

أسلوب هو العامة ليس فقط لأنه ينتمي إلى نوع عام ويستخدم معلمات نوع من هذا النوع.أسلوب هو عام فقط إذا كان لديه lهوt معلمة النوع الخاص به.يمكن أن يظهر أسلوب عام تشغيل نوع nongeneric، كـ في هذا المثال.تشغيل سبيل مثال أسلوب nongeneric تشغيل نوع عام، راجع كيفية القيام بما يلي: قم بتعريف نوع عام مع انعكاس حذف.

إلى تعريف أسلوب عام

  1. قبل البدء، فإنه هو مفيدة لمشاهدة كيفية ظهور الأسلوب العام عند كتابة باستخدام لغة عالية المستوى. التعليمة البرمجية التالية هو المضمنة في تعليمات برمجية مثال لترتيب هو الموضوع، بالإضافة إلى تعليمات برمجية استدعاء الأسلوب العام. يحتوي الأسلوب على نوع معلمتين، TInputو TOutput، الثاني الذي يجب أن يكون نوع المرجع ( class)، يجب أن يحتوي على الدالة الإنشائية بدون معلمات ( new)، ويجب أن يقوم تطبيق ICollection(Of TInput)(ICollection<TInput>في C#). يضمن هذا قيد واجهة ICollection<T>.Addأسلوب يمكن استخدامها لإضافة عناصر إلى TOutputمجموعة التي أسلوب ينشئ. يحتوي الأسلوب على معلمة رسمية واحدة، input، أي هو صفيفة من TInput. الأسلوب الذي يقوم بإنشاء مجموعة من نوع TOutputو قم بنسخ العناصر inputإلى مجموعة.

    Public Shared Function Factory(Of TInput, _
        TOutput As {ICollection(Of TInput), Class, New}) _
        (ByVal input() As TInput) As TOutput
    
        Dim retval As New TOutput()
        Dim ic As ICollection(Of TInput) = retval
    
        For Each t As TInput In input
            ic.Add(t)
        Next
    
        Return retval
    End Function 
    
    public static TOutput Factory<TInput, TOutput>(TInput[] tarray) 
        where TOutput : class, ICollection<TInput>, new()
    {
        TOutput ret = new TOutput();
        ICollection<TInput> ic = ret;
    
        foreach (TInput t in tarray)
        {
            ic.Add(t);
        }
        return ret;
    }
    
  2. تعريف تجميع حيوي ووحدة نمطية حيوية إلى تحتوي على نوع الأسلوب العام ينتمي إلى. في ترتيب هو هذه الحالة، يحتوي تجميع وحدة نمطية واحدة فقط، يسمى DemoMethodBuilder1، واسم وحدة نمطية? هو نفس اسم تجميع زائد على الملحق. في ترتيب هو سبيل المثال، تجميع هو تم الحفظ dهوk وأيضا تنفيذها، وفي هذه الحالة AssemblyBuilderAccess.RunAndSaveهو المحدد. يمكنك استخدام Ildasm.exe (المفكك MSIL)إلى فحص DemoMethodBuilder1.dll و إلى قارنة إلى Microsoft Office 2010 Suite المتوسطة لغة (MSIL) الأسلوب هو موضح في الخطوة 1.

    Dim asmName As New AssemblyName("DemoMethodBuilder1")
    Dim domain As AppDomain = AppDomain.CurrentDomain
    Dim demoAssembly As AssemblyBuilder = _
        domain.DefineDynamicAssembly(asmName, _
            AssemblyBuilderAccess.RunAndSave)
    
    ' Define the module that contains the code. For an 
    ' assembly with one module, the module name is the 
    ' assembly name plus a file extension.
    Dim demoModule As ModuleBuilder = _
        demoAssembly.DefineDynamicModule( _
            asmName.Name, _
            asmName.Name & ".dll")
    
    AssemblyName asmName = new AssemblyName("DemoMethodBuilder1");
    AppDomain domain = AppDomain.CurrentDomain;
    AssemblyBuilder demoAssembly = 
        domain.DefineDynamicAssembly(asmName, 
            AssemblyBuilderAccess.RunAndSave);
    
    // Define the module that contains the code. For an 
    // assembly with one module, the module name is the 
    // assembly name plus a file extension.
    ModuleBuilder demoModule = 
        demoAssembly.DefineDynamicModule(asmName.Name, 
            asmName.Name+".dll");
    
  3. تعريف نوع الأسلوب العام ينتمي إلى. نوع لا يجب أن يكون عاماً. يمكن أن تنتمي أسلوب عام إلى أما عامة أو nongeneric نوع. في هذا المثال، نوع فئة غير عام ويسمى DemoType.

    Dim demoType As TypeBuilder = demoModule.DefineType( _
        "DemoType", _
        TypeAttributes.Public) 
    
    TypeBuilder demoType = 
        demoModule.DefineType("DemoType", TypeAttributes.Public);
    
  4. تعرف أسلوب عام. إذا رسمية s نوع من الأسلوب العام المعلمات المحددة قبل عام نوع استخدم المعلمات الخاصة بالأسلوب العام، DefineMethod(String, MethodAttributes)أسلوب زيادة التحميل لتعريف الأسلوب. معلمات الأسلوب النوع العام لم يتم حتى الآن تعريف، لذا لا يمكن تحديد أنواع المعلمات الأساسية للأسلوب في استدعاء إلى DefineMethod. في this مثال, the أسلوب هو named Factory. The أسلوب هو public و static (Shared في Visual أساسى).

    Dim factory As MethodBuilder = _
        demoType.DefineMethod("Factory", _
            MethodAttributes.Public Or MethodAttributes.Static)
    
    MethodBuilder factory = 
        demoType.DefineMethod("Factory", 
            MethodAttributes.Public | MethodAttributes.Static);
    
  5. تعريف معلمة نوع عام من DemoMethodبتمرير صفيفة سلاسل التي تحتوي على أسماء معلمات إلى MethodBuilder.DefineGenericParametersالأسلوب. This makes the أسلوب a generic أسلوب. يجعل التعليمة البرمجية التالية Factoryأسلوب عام مع معلمات نوع TInputو TOutput. لتسهيل قراءة تعليمات برمجية، يتم إنشاء المتغيرات التي تحتوي على هذه الأسماء لاحتواء GenericTypeParameterBuilderالكائنات التي تمثل الاثنين معلمات نوع.

    Dim typeParameterNames() As String = {"TInput", "TOutput"}
    Dim typeParameters() As GenericTypeParameterBuilder = _
        factory.DefineGenericParameters(typeParameterNames)
    
    Dim TInput As GenericTypeParameterBuilder = typeParameters(0)
    Dim TOutput As GenericTypeParameterBuilder = typeParameters(1)
    
    string[] typeParameterNames = {"TInput", "TOutput"};
    GenericTypeParameterBuilder[] typeParameters = 
        factory.DefineGenericParameters(typeParameterNames);
    
    GenericTypeParameterBuilder TInput = typeParameters[0];
    GenericTypeParameterBuilder TOutput = typeParameters[1];
    
  6. Optionally إضافة خاص constraints إلى the نوع معلمات. خاص constraints are تمت الإضافة using the SetGenericParameterAttributes أسلوب. في this مثال, TOutput هو constrained إلى be a مرجع نوع و إلى have a parameterless الدالة الإنشائية.

    TOutput.SetGenericParameterAttributes( _
        GenericParameterAttributes.ReferenceTypeConstraint Or _
        GenericParameterAttributes.DefaultConstructorConstraint)
    
    TOutput.SetGenericParameterAttributes(
        GenericParameterAttributes.ReferenceTypeConstraint | 
        GenericParameterAttributes.DefaultConstructorConstraint);
    
  7. بشكل اختياري إضافة قيود الفئة وواجهة إلى نوع المعلمات. في this مثال, نوع معلمة TOutput هو constrained إلى أنواع that implement the ICollection(Of TInput) (ICollection<TInput> في C#) واجهة. This ensures that the Add أسلوب can be used إلى إضافة عناصر.

    Dim icoll As Type = GetType(ICollection(Of ))
    Dim icollOfTInput As Type = icoll.MakeGenericType(TInput)
    Dim constraints() As Type = { icollOfTInput }
    TOutput.SetInterfaceConstraints(constraints)
    
    Type icoll = typeof(ICollection<>);
    Type icollOfTInput = icoll.MakeGenericType(TInput);
    Type[] constraints = {icollOfTInput};
    TOutput.SetInterfaceConstraints(constraints);
    
  8. Define the formal معلمات of the أسلوب, using the SetParameters أسلوب. في هذا المثال، Factoryالأسلوب على معلمة واحدة، صفيفة من TInput. This نوع هو تاريخ الإنشاء بواسطة calling the MakeArrayType أسلوب تشغيل the GenericTypeParameterBuilder that represents TInput. The وسيطة of SetParameters هو an مصفوفه من Type الكائنات.

    Dim params() As Type = { TInput.MakeArrayType() }
    factory.SetParameters(params)
    
    Type[] parms = {TInput.MakeArrayType()};
    factory.SetParameters(parms);
    
  9. Define the return نوع for the أسلوب, using the SetReturnType أسلوب. في ترتيب هو سبيل المثال، مثيل TOutputهو التي يتم إرجاعها.

    factory.SetReturnType(TOutput)
    
    factory.SetReturnType(TOutput);
    
  10. الإرسال أسلوب نص، باستخدام ILGenerator. للحصول على التفاصيل، انظر المرافق إجراء إلى Emit نص أسلوب .

    ملاحظة هامةهام

    When you emit calls to methods of generic types, and the type arguments of those types are type parameters of the generic method, you must use the static GetConstructor(Type, ConstructorInfo), GetMethod(Type, MethodInfo), and GetField(Type, FieldInfo) method overloads of the TypeBuilder class to obtain constructed forms of the methods.إجراء المرافق باعث أسلوب نص يوضح ذلك.

  11. أكمل نوع التي تحتوي على الأسلوب وحفظ تجميع. تظهر المرافق إجراء إلى استدعاء "أسلوب عام" طريقتين استدعاء الأسلوب المكتملة.

    ' Complete the type.
    Dim dt As Type = demoType.CreateType()
    ' Save the assembly, so it can be examined with Ildasm.exe.
    demoAssembly.Save(asmName.Name & ".dll")
    
    // Complete the type.
    Type dt = demoType.CreateType();
    // Save the assembly, so it can be examined with Ildasm.exe.
    demoAssembly.Save(asmName.Name+".dll");
    

إلى نص أسلوب الإرسال

  1. يحصل تعليمات برمجية مولد وتقوم بتعريف المتغيرات المحلية والتسميات. DeclareLocalالأسلوب هو المستخدم لتعريف المتغيرات المحلية. Factoryأسلوب يحتوي على الرابع متغيرات محلية: retValللاحتفاظ بالجديدTOutputالتي هو يتم إرجاعها بواسطة الأسلوب،icللاحتفاظTOutputعند ذلك هو تحويل إلى ICollection(Of TInput)(ICollection<TInput>في C#),inputلاحتواء صفيفة إدخالTInputالكائنات، وindexلتكرار إلى الصفيفة. يحتوي الأسلوب على تسميات الثاني، واحد لإدخال أيضا تكرار حلقي ( enterLoop) وآخر للجزء العلوي من تكرار حلقي ( loopAgain) المعرف باستخدام DefineLabelالأسلوب.

    أول شيء إلى الأسلوب الذي يتم تحميل وسيطة الخاصة به باستخدام Ldarg_0شفرة التشغيل وتخزينه في متغير محلي inputاستخدام Stloc_Sاختصارات رموز العمليات.

    Dim ilgen As ILGenerator = factory.GetILGenerator()
    
    Dim retVal As LocalBuilder = ilgen.DeclareLocal(TOutput)
    Dim ic As LocalBuilder = ilgen.DeclareLocal(icollOfTInput)
    Dim input As LocalBuilder = _
        ilgen.DeclareLocal(TInput.MakeArrayType())
    Dim index As LocalBuilder = _
        ilgen.DeclareLocal(GetType(Integer))
    
    Dim enterLoop As Label = ilgen.DefineLabel()
    Dim loopAgain As Label = ilgen.DefineLabel()
    
    ilgen.Emit(OpCodes.Ldarg_0)
    ilgen.Emit(OpCodes.Stloc_S, input)
    
    ILGenerator ilgen = factory.GetILGenerator();
    
    LocalBuilder retVal = ilgen.DeclareLocal(TOutput);
    LocalBuilder ic = ilgen.DeclareLocal(icollOfTInput);
    LocalBuilder input = ilgen.DeclareLocal(TInput.MakeArrayType());
    LocalBuilder index = ilgen.DeclareLocal(typeof(int));
    
    Label enterLoop = ilgen.DefineLabel();
    Label loopAgain = ilgen.DefineLabel();
    
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Stloc_S, input);
    
  2. الإرسال تعليمات برمجية إنشاء مثيل ل TOutput، استخدام التحميل الزائد للأسلوب العام Activator.CreateInstanceالأسلوب. استخدام ترتيب هو يتطلب التحميل الزائد للنوع المحدد للحصول على الدالة الإنشائية بدون معلمات، الذي هو السبب في إضافة هذا قيد إلى TOutput. إنشاء أسلوب عام constructed بتمرير TOutputإلى MakeGenericMethod. بعد باعث تعليمات برمجية الإرسال باستدعاء الأسلوب، تعليمات برمجية تخزينه في retValاستخدامStloc_S متغير محلي

    Dim createInst As MethodInfo = _
        GetType(Activator).GetMethod("CreateInstance", Type.EmptyTypes)
    Dim createInstOfTOutput As MethodInfo = _
        createInst.MakeGenericMethod(TOutput)
    
    ilgen.Emit(OpCodes.Call, createInstOfTOutput)
    ilgen.Emit(OpCodes.Stloc_S, retVal)
    
    MethodInfo createInst = 
        typeof(Activator).GetMethod("CreateInstance", Type.EmptyTypes);
    MethodInfo createInstOfTOutput = 
        createInst.MakeGenericMethod(TOutput);
    
    ilgen.Emit(OpCodes.Call, createInstOfTOutput);
    ilgen.Emit(OpCodes.Stloc_S, retVal);
    
  3. الإرسال تعليمات برمجية تحويل من جديد TOutputالكائن إلى ICollection(Of TInput)وتخزينها في متغير محلي ic.

    ilgen.Emit(OpCodes.Ldloc_S, retVal)
    ilgen.Emit(OpCodes.Box, TOutput)
    ilgen.Emit(OpCodes.Castclass, icollOfTInput)
    ilgen.Emit(OpCodes.Stloc_S, ic)
    
    ilgen.Emit(OpCodes.Ldloc_S, retVal);
    ilgen.Emit(OpCodes.Box, TOutput);
    ilgen.Emit(OpCodes.Castclass, icollOfTInput);
    ilgen.Emit(OpCodes.Stloc_S, ic);
    
  4. يحصل MethodInfoالتي تمثل ICollection<T>.Addأسلوب. الأسلوب هو التي تعمل تشغيل ICollection(Of TInput)(ICollection<TInput>في C#) ، ولذلك هو اللازمة للحصول تشغيل Addطريقة معينة بإنشاء النوع. لا يمكنك استخدام GetMethodطريقة للحصول تشغيل رقم هو MethodInfoمباشرة من icollOfTInput، لأن GetMethodهو غير معتمدة تشغيل نوع الذي تم إنشاء باستخدام GenericTypeParameterBuilder. بدلاً من ذلك، يتصل GetMethodتشغيل icoll، الذي يحتوي تشغيل تعريف نوع عام ICollection<T>واجهة عامة. Then use the GetMethod(Type, MethodInfo) static method to produce the MethodInfo for the constructed type. يلي تعليمات برمجية يوضح ذلك.

    Dim mAddPrep As MethodInfo = icoll.GetMethod("Add")
    Dim mAdd As MethodInfo = _
        TypeBuilder.GetMethod(icollOfTInput, mAddPrep)
    
    MethodInfo mAddPrep = icoll.GetMethod("Add");
    MethodInfo mAdd = TypeBuilder.GetMethod(icollOfTInput, mAddPrep);
    
  5. الإرسال تعليمات برمجية ليهيّئ indexالمتغير، بواسطة تحميل عددا صحيحاً 32 بت 0 وتخزينها في متغير. إرسال تعليمات برمجية إلى فرع ل التسمية enterLoop. Th هو التسمية لا بعد تم وضع علامة على، لأن ذلك هو داخل الحلقة. تعليمات برمجية للحلقة هو تصدر في الخطوة التالية.

    ' Initialize the count and enter the loop.
    ilgen.Emit(OpCodes.Ldc_I4_0)
    ilgen.Emit(OpCodes.Stloc_S, index)
    ilgen.Emit(OpCodes.Br_S, enterLoop)
    
    // Initialize the count and enter the loop.
    ilgen.Emit(OpCodes.Ldc_I4_0);
    ilgen.Emit(OpCodes.Stloc_S, index);
    ilgen.Emit(OpCodes.Br_S, enterLoop);
    
  6. التعليمات البرمجية للإرسال تكرار حلقي. الخطوة الأولى هو لوضع علامة على الأعلى من الحلقة، بواسطة استدعاء MarkLabelمع loopAgainالتسمية. سيتم الآن التفرع عبارات الفرع التي تستخدم التسمية إلى هذه النقطة في تعليمات برمجية. الخطوة التالية هو لدفع TOutputالكائن، إضافة إلى ICollection(Of TInput)، إلى المكدس. هو لا يلزم مباشرة، ولكن يجب أن يكون في موقع للاتصال في Addالأسلوب. التالي صفيفة إدخال هو دفع إلى بنية تخزين العناصر، ثم indexالمتغير الذي يحتوي على الفهرس الحالي في الصفيفة. Ldelemيخرج شفرة التشغيل الفهرس الصفيف مكدس ويدفع عنصر الصفيف المفهرسة إلى مكدس. المكدس هو الآن جاهزة للاستدعاء إلى ICollection<T>.Addالأسلوب الذي يخرج المجموعة والعنصر الجديد إيقاف المكدس وقم بإضافة عنصر إلى مجموعة.

    الجزء المتبقي من تعليمات برمجية في الحلقة بزيادة الفهرس واختبارات لمعرفة ما إذا كان الحلقة هو finهوhed: دفع إلى المكدس الفهرس وعددا صحيحاً 32 بت 1 وإضافتها، مع ترك جمع تشغيل المكدس؛ جمع هو المخزنة في index. MarkLabelهو استدعاء لتعيين ترتيب هو نقطة كنقطة إدخال لحلقة. الفهرس هو تم تحميلهه مرة أخرى. يتم دفع صفيفة إدخال تشغيل المكدس و Ldlenهو إصدار إلى تشغيل الطول به. الفهرس والطول يتم الآن تشغيل المكدس و Cltصدور يقارن بها. إذا كان الفهرس هو أصغر من من الطول Brtrue_Sالفروع إلى بداية الحلقة.

    ilgen.MarkLabel(loopAgain)
    
    ilgen.Emit(OpCodes.Ldloc_S, ic)
    ilgen.Emit(OpCodes.Ldloc_S, input)
    ilgen.Emit(OpCodes.Ldloc_S, index)
    ilgen.Emit(OpCodes.Ldelem, TInput)
    ilgen.Emit(OpCodes.Callvirt, mAdd)
    
    ilgen.Emit(OpCodes.Ldloc_S, index)
    ilgen.Emit(OpCodes.Ldc_I4_1)
    ilgen.Emit(OpCodes.Add)
    ilgen.Emit(OpCodes.Stloc_S, index)
    
    ilgen.MarkLabel(enterLoop)
    ilgen.Emit(OpCodes.Ldloc_S, index)
    ilgen.Emit(OpCodes.Ldloc_S, input)
    ilgen.Emit(OpCodes.Ldlen)
    ilgen.Emit(OpCodes.Conv_I4)
    ilgen.Emit(OpCodes.Clt)
    ilgen.Emit(OpCodes.Brtrue_S, loopAgain)
    
    ilgen.MarkLabel(loopAgain);
    
    ilgen.Emit(OpCodes.Ldloc_S, ic);
    ilgen.Emit(OpCodes.Ldloc_S, input);
    ilgen.Emit(OpCodes.Ldloc_S, index);
    ilgen.Emit(OpCodes.Ldelem, TInput);
    ilgen.Emit(OpCodes.Callvirt, mAdd);
    
    ilgen.Emit(OpCodes.Ldloc_S, index);
    ilgen.Emit(OpCodes.Ldc_I4_1);
    ilgen.Emit(OpCodes.Add);
    ilgen.Emit(OpCodes.Stloc_S, index);
    
    ilgen.MarkLabel(enterLoop);
    ilgen.Emit(OpCodes.Ldloc_S, index);
    ilgen.Emit(OpCodes.Ldloc_S, input);
    ilgen.Emit(OpCodes.Ldlen);
    ilgen.Emit(OpCodes.Conv_I4);
    ilgen.Emit(OpCodes.Clt);
    ilgen.Emit(OpCodes.Brtrue_S, loopAgain);
    
  7. الإرسال تعليمات برمجية لدفع TOutputالكائن في مكدس الذاكرة المؤقتة والعائد من أسلوب. المحلي retValمتغيرات ؛icكلاهما يحتوي على مراجع إلى الجديدةTOutput icهو المستخدمة للوصول فقط ICollection<T>.Addالأسلوب.

    ilgen.Emit(OpCodes.Ldloc_S, retVal)
    ilgen.Emit(OpCodes.Ret)
    
    ilgen.Emit(OpCodes.Ldloc_S, retVal);
    ilgen.Emit(OpCodes.Ret);
    

إلى استدعاء الأسلوب العام

  1. Factoryهو أسلوب عام تعريف. في ترتيب إلى استدعاء عليه، يجب تعيين أنواع إلى معلمات النوع العام الخاص به. استخدام MakeGenericMethodأسلوب إلى القيام بذلك. إنشاء تعليمات برمجية التالية constructed أسلوب عام، تعيين Stringل TInputو List(Of String)(List<string>في C#) ل TOutput، وقم بعرض تمثيل سلسلة للأسلوب.

    Dim m As MethodInfo = dt.GetMethod("Factory")
    Dim bound As MethodInfo = m.MakeGenericMethod( _
        GetType(String), GetType(List(Of String)))
    
    ' Display a string representing the bound method.
    Console.WriteLine(bound)
    
    MethodInfo m = dt.GetMethod("Factory");
    MethodInfo bound = 
        m.MakeGenericMethod(typeof(string), typeof(List<string>));
    
    // Display a string representing the bound method.
    Console.WriteLine(bound);
    
  2. إلى استدعاء الأسلوب المتأخر-الحد، استخدم Invokeالأسلوب. يلي تعليمات برمجية إنشاء صفيفة من Objectوالذي يتضمن العنصر الوحيد الخاص به صفيفة سلاسل، ويمرره كقائمة وسيطة للأسلوب العام. معلمة الأولى من Invokeلأنه مرجع فارغ أسلوب هو static. قيمة الإرجاع هو تحويل إلى List(Of String)، والعنصر أول الخاص به هو dهوplayed.

    Dim o As Object = bound.Invoke(Nothing, New Object() { arr })
    Dim list2 As List(Of String) = CType(o, List(Of String))
    
    Console.WriteLine("The first element is: {0}", list2(0))
    
    object o = bound.Invoke(null, new object[]{arr});
    List<string> list2 = (List<string>) o;
    
    Console.WriteLine("The first element is: {0}", list2[0]);
    
  3. إلى استدعاء أسلوب استخدام مفوض، يجب أن يكون لديك مفوض الذي يطابق توقيع الأسلوب عام constructed. طريقة سهلة للقيام بترتيب هو هو لإنشاء مفوض عام. التعليمة البرمجية التالية بإنشاء مثيل ل تفويض عام Dالمعرفة في تعليمات برمجية المثال، استخدام Delegate.CreateDelegate(Type, MethodInfo)أسلوب التحميل الزائد، و استدعاء المفوض. المفوضون أداء أفضل من المتأخر-حدود المكالمات.

    Dim dType As Type = GetType(D(Of String, List(Of String)))
    Dim test As D(Of String, List(Of String))
    test = CType( _
        [Delegate].CreateDelegate(dType, bound), _
        D(Of String, List(Of String)))
    
    Dim list3 As List(Of String) = test(arr)
    Console.WriteLine("The first element is: {0}", list3(0))
    
    Type dType = typeof(D<string, List <string>>);
    D<string, List <string>> test;
    test = (D<string, List <string>>) 
        Delegate.CreateDelegate(dType, bound);
    
    List<string> list3 = test(arr);
    Console.WriteLine("The first element is: {0}", list3[0]);
    
  4. emitted أسلوب يمكن استدعاء من أحد برامج التي تشير إلى تجميع المحفوظة.

مثال

يلي تعليمات برمجية المثال، يتم إنشاء نوع nongeneric DemoType، بأسلوب عام، Factory. هذا الأسلوب على نوع عام معلمتين، TInputإلى تعيين نوع إدخال غير و TOutputإلى تحديد نوع الإخراج على. TOutputاكتب المعلمة هو مقيدة بتنفيذ ICollection<TInput>(ICollection(Of TInput)في Vهوual أساسى) ، يكون نوع مرجع، ولكي يقوم الدالة الإنشائية بدون معلمات.

يحتوي الأسلوب على معلمة رسمية واحدة، مما هو صفيفة من TInput. أسلوب إرجاع مثيل TOutputيحتوي على الجميع العناصر الموجودة في صفيفة إدخال. TOutputيمكن أن يكون أي نوع المجموعة العامة التي تطبقICollection<T>واجهة عامة.

عند التعليمة البرمجية هو التنفيذ، تجميع حيوي هو حفظها ك DemoGenericMethod1.dll، ويمكن أن يكون فحص باستخدام Ildasm.exe (المفكك MSIL).

ملاحظةملاحظة

طريقة بضاعة للتعرف على كيفية إرسال رمز هو لكتابة برنامج Vهوual أساسى أو C# أو C + + Vهوual يقوم بأداء مهمة التي تحاول الإرسال، وقم باستخدام dهوassembler لفحص MSIL المنتجة من قبل المحول البرمجي.

مثال التعليمة البرمجية تتضمن تعليمات برمجية المصدر التي هو مساوية للأسلوب emitted. تم استدعاء الأسلوب emitted منضم المتأخرة وأيضا باستخدام مفوض عام معلن تعليمات برمجية المثال.

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Reflection.Emit

' Declare a generic delegate that can be used to execute the 
' finished method.
'
Delegate Function D(Of TIn, TOut)(ByVal input() As TIn) As TOut

Class GenericMethodBuilder

    ' This method shows how to declare, in Visual Basic, the generic
    ' method this program emits. The method has two type parameters,
    ' TInput and TOutput, the second of which must be a reference type
    ' (Class), must have a parameterless constructor (New), and must
    ' implement ICollection(Of TInput). This interface constraint
    ' ensures that ICollection(Of TInput).Add can be used to add
    ' elements to the TOutput object the method creates. The method 
    ' has one formal parameter, input, which is an array of TInput. 
    ' The elements of this array are copied to the new TOutput.
    '
    Public Shared Function Factory(Of TInput, _
        TOutput As {ICollection(Of TInput), Class, New}) _
        (ByVal input() As TInput) As TOutput

        Dim retval As New TOutput()
        Dim ic As ICollection(Of TInput) = retval

        For Each t As TInput In input
            ic.Add(t)
        Next

        Return retval
    End Function 


    Public Shared Sub Main()
        ' The following shows the usage syntax of the Visual Basic
        ' version of the generic method emitted by this program.
        ' Note that the generic parameters must be specified 
        ' explicitly, because the compiler does not have enough 
        ' context to infer the type of TOutput. In this case, TOutput
        ' is a generic List containing strings.
        ' 
        Dim arr() As String = {"a", "b", "c", "d", "e"}
        Dim list1 As List(Of String) = _
            GenericMethodBuilder.Factory(Of String, List(Of String))(arr)
        Console.WriteLine("The first element is: {0}", list1(0))


        ' Creating a dynamic assembly requires an AssemblyName
        ' object, and the current application domain.
        '
        Dim asmName As New AssemblyName("DemoMethodBuilder1")
        Dim domain As AppDomain = AppDomain.CurrentDomain
        Dim demoAssembly As AssemblyBuilder = _
            domain.DefineDynamicAssembly(asmName, _
                AssemblyBuilderAccess.RunAndSave)

        ' Define the module that contains the code. For an 
        ' assembly with one module, the module name is the 
        ' assembly name plus a file extension.
        Dim demoModule As ModuleBuilder = _
            demoAssembly.DefineDynamicModule( _
                asmName.Name, _
                asmName.Name & ".dll")

        ' Define a type to contain the method.
        Dim demoType As TypeBuilder = demoModule.DefineType( _
            "DemoType", _
            TypeAttributes.Public) 

        ' Define a Shared, Public method with standard calling
        ' conventions. Do not specify the parameter types or the
        ' return type, because type parameters will be used for 
        ' those types, and the type parameters have not been
        ' defined yet.
        '
        Dim factory As MethodBuilder = _
            demoType.DefineMethod("Factory", _
                MethodAttributes.Public Or MethodAttributes.Static)

        ' Defining generic type parameters for the method makes it a
        ' generic method. To make the code easier to read, each
        ' type parameter is copied to a variable of the same name.
        '
        Dim typeParameterNames() As String = {"TInput", "TOutput"}
        Dim typeParameters() As GenericTypeParameterBuilder = _
            factory.DefineGenericParameters(typeParameterNames)

        Dim TInput As GenericTypeParameterBuilder = typeParameters(0)
        Dim TOutput As GenericTypeParameterBuilder = typeParameters(1)

        ' Add special constraints.
        ' The type parameter TOutput is constrained to be a reference
        ' type, and to have a parameterless constructor. This ensures
        ' that the Factory method can create the collection type.
        ' 
        TOutput.SetGenericParameterAttributes( _
            GenericParameterAttributes.ReferenceTypeConstraint Or _
            GenericParameterAttributes.DefaultConstructorConstraint)

        ' Add interface and base type constraints.
        ' The type parameter TOutput is constrained to types that
        ' implement the ICollection(Of T) interface, to ensure that
        ' they have an Add method that can be used to add elements.
        '
        ' To create the constraint, first use MakeGenericType to bind 
        ' the type parameter TInput to the ICollection(Of T) interface,
        ' returning the type ICollection(Of TInput), then pass
        ' the newly created type to the SetInterfaceConstraints
        ' method. The constraints must be passed as an array, even if
        ' there is only one interface.
        '
        Dim icoll As Type = GetType(ICollection(Of ))
        Dim icollOfTInput As Type = icoll.MakeGenericType(TInput)
        Dim constraints() As Type = { icollOfTInput }
        TOutput.SetInterfaceConstraints(constraints)

        ' Set parameter types for the method. The method takes
        ' one parameter, an array of type TInput.
        Dim params() As Type = { TInput.MakeArrayType() }
        factory.SetParameters(params)

        ' Set the return type for the method. The return type is
        ' the generic type parameter TOutput.
        factory.SetReturnType(TOutput)

        ' Generate a code body for the method. 
        ' -----------------------------------
        ' Get a code generator and declare local variables and
        ' labels. Save the input array to a local variable.
        '
        Dim ilgen As ILGenerator = factory.GetILGenerator()

        Dim retVal As LocalBuilder = ilgen.DeclareLocal(TOutput)
        Dim ic As LocalBuilder = ilgen.DeclareLocal(icollOfTInput)
        Dim input As LocalBuilder = _
            ilgen.DeclareLocal(TInput.MakeArrayType())
        Dim index As LocalBuilder = _
            ilgen.DeclareLocal(GetType(Integer))

        Dim enterLoop As Label = ilgen.DefineLabel()
        Dim loopAgain As Label = ilgen.DefineLabel()

        ilgen.Emit(OpCodes.Ldarg_0)
        ilgen.Emit(OpCodes.Stloc_S, input)

        ' Create an instance of TOutput, using the generic method 
        ' overload of the Activator.CreateInstance method. 
        ' Using this overload requires the specified type to have
        ' a parameterless constructor, which is the reason for adding 
        ' that constraint to TOutput. Create the constructed generic
        ' method by passing TOutput to MakeGenericMethod. After
        ' emitting code to call the method, emit code to store the
        ' new TOutput in a local variable. 
        '
        Dim createInst As MethodInfo = _
            GetType(Activator).GetMethod("CreateInstance", Type.EmptyTypes)
        Dim createInstOfTOutput As MethodInfo = _
            createInst.MakeGenericMethod(TOutput)

        ilgen.Emit(OpCodes.Call, createInstOfTOutput)
        ilgen.Emit(OpCodes.Stloc_S, retVal)

        ' Load the reference to the TOutput object, cast it to
        ' ICollection(Of TInput), and save it.
        ilgen.Emit(OpCodes.Ldloc_S, retVal)
        ilgen.Emit(OpCodes.Box, TOutput)
        ilgen.Emit(OpCodes.Castclass, icollOfTInput)
        ilgen.Emit(OpCodes.Stloc_S, ic)

        ' Loop through the array, adding each element to the new
        ' instance of TOutput. Note that in order to get a MethodInfo
        ' for ICollection(Of TInput).Add, it is necessary to first 
        ' get the Add method for the generic type defintion,
        ' ICollection(Of T).Add. This is because it is not possible
        ' to call GetMethod on icollOfTInput. The static overload of
        ' TypeBuilder.GetMethod produces the correct MethodInfo for
        ' the constructed type.
        '
        Dim mAddPrep As MethodInfo = icoll.GetMethod("Add")
        Dim mAdd As MethodInfo = _
            TypeBuilder.GetMethod(icollOfTInput, mAddPrep)

        ' Initialize the count and enter the loop.
        ilgen.Emit(OpCodes.Ldc_I4_0)
        ilgen.Emit(OpCodes.Stloc_S, index)
        ilgen.Emit(OpCodes.Br_S, enterLoop)

        ' Mark the beginning of the loop. Push the ICollection
        ' reference on the stack, so it will be in position for the
        ' call to Add. Then push the array and the index on the 
        ' stack, get the array element, and call Add (represented
        ' by the MethodInfo mAdd) to add it to the collection.
        ' 
        ' The other ten instructions just increment the index
        ' and test for the end of the loop. Note the MarkLabel
        ' method, which sets the point in the code where the 
        ' loop is entered. (See the earlier Br_S to enterLoop.)
        '
        ilgen.MarkLabel(loopAgain)

        ilgen.Emit(OpCodes.Ldloc_S, ic)
        ilgen.Emit(OpCodes.Ldloc_S, input)
        ilgen.Emit(OpCodes.Ldloc_S, index)
        ilgen.Emit(OpCodes.Ldelem, TInput)
        ilgen.Emit(OpCodes.Callvirt, mAdd)

        ilgen.Emit(OpCodes.Ldloc_S, index)
        ilgen.Emit(OpCodes.Ldc_I4_1)
        ilgen.Emit(OpCodes.Add)
        ilgen.Emit(OpCodes.Stloc_S, index)

        ilgen.MarkLabel(enterLoop)
        ilgen.Emit(OpCodes.Ldloc_S, index)
        ilgen.Emit(OpCodes.Ldloc_S, input)
        ilgen.Emit(OpCodes.Ldlen)
        ilgen.Emit(OpCodes.Conv_I4)
        ilgen.Emit(OpCodes.Clt)
        ilgen.Emit(OpCodes.Brtrue_S, loopAgain)

        ilgen.Emit(OpCodes.Ldloc_S, retVal)
        ilgen.Emit(OpCodes.Ret)

        ' Complete the type.
        Dim dt As Type = demoType.CreateType()
        ' Save the assembly, so it can be examined with Ildasm.exe.
        demoAssembly.Save(asmName.Name & ".dll")

        ' To create a constructed generic method that can be
        ' executed, first call the GetMethod method on the completed 
        ' type to get the generic method definition. Call MakeGenericType
        ' on the generic method definition to obtain the constructed
        ' method, passing in the type arguments. In this case, the
        ' constructed method has String for TInput and List(Of String)
        ' for TOutput. 
        '
        Dim m As MethodInfo = dt.GetMethod("Factory")
        Dim bound As MethodInfo = m.MakeGenericMethod( _
            GetType(String), GetType(List(Of String)))

        ' Display a string representing the bound method.
        Console.WriteLine(bound)


        ' Once the generic method is constructed, 
        ' you can invoke it and pass in an array of objects 
        ' representing the arguments. In this case, there is only
        ' one element in that array, the argument 'arr'.
        '
        Dim o As Object = bound.Invoke(Nothing, New Object() { arr })
        Dim list2 As List(Of String) = CType(o, List(Of String))

        Console.WriteLine("The first element is: {0}", list2(0))


        ' You can get better performance from multiple calls if
        ' you bind the constructed method to a delegate. The 
        ' following code uses the generic delegate D defined 
        ' earlier.
        '
        Dim dType As Type = GetType(D(Of String, List(Of String)))
        Dim test As D(Of String, List(Of String))
        test = CType( _
            [Delegate].CreateDelegate(dType, bound), _
            D(Of String, List(Of String)))

        Dim list3 As List(Of String) = test(arr)
        Console.WriteLine("The first element is: {0}", list3(0))

    End Sub  
End Class 

' This code example produces the following output:
'
'The first element is: a
'System.Collections.Generic.List`1[System.String] Factory[String,List`1](System.String[])
'The first element is: a
'The first element is: a
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

// Declare a generic delegate that can be used to execute the 
// finished method.
//
public delegate TOut D<TIn, TOut>(TIn[] input);

class GenericMethodBuilder
{
    // This method shows how to declare, in Visual Basic, the generic
    // method this program emits. The method has two type parameters,
    // TInput and TOutput, the second of which must be a reference type
    // (class), must have a parameterless constructor (new()), and must
    // implement ICollection<TInput>. This interface constraint
    // ensures that ICollection<TInput>.Add can be used to add
    // elements to the TOutput object the method creates. The method 
    // has one formal parameter, input, which is an array of TInput. 
    // The elements of this array are copied to the new TOutput.
    //
    public static TOutput Factory<TInput, TOutput>(TInput[] tarray) 
        where TOutput : class, ICollection<TInput>, new()
    {
        TOutput ret = new TOutput();
        ICollection<TInput> ic = ret;

        foreach (TInput t in tarray)
        {
            ic.Add(t);
        }
        return ret;
    }

    public static void Main()
    {
        // The following shows the usage syntax of the C#‎
        // version of the generic method emitted by this program.
        // Note that the generic parameters must be specified 
        // explicitly, because the compiler does not have enough 
        // context to infer the type of TOutput. In this case, TOutput
        // is a generic List containing strings.
        // 
        string[] arr = {"a", "b", "c", "d", "e"};
        List<string> list1 = 
            GenericMethodBuilder.Factory<string, List <string>>(arr);
        Console.WriteLine("The first element is: {0}", list1[0]);


        // Creating a dynamic assembly requires an AssemblyName
        // object, and the current application domain.
        //
        AssemblyName asmName = new AssemblyName("DemoMethodBuilder1");
        AppDomain domain = AppDomain.CurrentDomain;
        AssemblyBuilder demoAssembly = 
            domain.DefineDynamicAssembly(asmName, 
                AssemblyBuilderAccess.RunAndSave);

        // Define the module that contains the code. For an 
        // assembly with one module, the module name is the 
        // assembly name plus a file extension.
        ModuleBuilder demoModule = 
            demoAssembly.DefineDynamicModule(asmName.Name, 
                asmName.Name+".dll");

        // Define a type to contain the method.
        TypeBuilder demoType = 
            demoModule.DefineType("DemoType", TypeAttributes.Public);

        // Define a public static method with standard calling
        // conventions. Do not specify the parameter types or the
        // return type, because type parameters will be used for 
        // those types, and the type parameters have not been
        // defined yet.
        //
        MethodBuilder factory = 
            demoType.DefineMethod("Factory", 
                MethodAttributes.Public | MethodAttributes.Static);

        // Defining generic type parameters for the method makes it a
        // generic method. To make the code easier to read, each
        // type parameter is copied to a variable of the same name.
        //
        string[] typeParameterNames = {"TInput", "TOutput"};
        GenericTypeParameterBuilder[] typeParameters = 
            factory.DefineGenericParameters(typeParameterNames);

        GenericTypeParameterBuilder TInput = typeParameters[0];
        GenericTypeParameterBuilder TOutput = typeParameters[1];

        // Add special constraints.
        // The type parameter TOutput is constrained to be a reference
        // type, and to have a parameterless constructor. This ensures
        // that the Factory method can create the collection type.
        // 
        TOutput.SetGenericParameterAttributes(
            GenericParameterAttributes.ReferenceTypeConstraint | 
            GenericParameterAttributes.DefaultConstructorConstraint);

        // Add interface and base type constraints.
        // The type parameter TOutput is constrained to types that
        // implement the ICollection<T> interface, to ensure that
        // they have an Add method that can be used to add elements.
        //
        // To create the constraint, first use MakeGenericType to bind 
        // the type parameter TInput to the ICollection<T> interface,
        // returning the type ICollection<TInput>, then pass
        // the newly created type to the SetInterfaceConstraints
        // method. The constraints must be passed as an array, even if
        // there is only one interface.
        //
        Type icoll = typeof(ICollection<>);
        Type icollOfTInput = icoll.MakeGenericType(TInput);
        Type[] constraints = {icollOfTInput};
        TOutput.SetInterfaceConstraints(constraints);

        // Set parameter types for the method. The method takes
        // one parameter, an array of type TInput.
        Type[] parms = {TInput.MakeArrayType()};
        factory.SetParameters(parms);

        // Set the return type for the method. The return type is
        // the generic type parameter TOutput.
        factory.SetReturnType(TOutput);

        // Generate a code body for the method. 
        // -----------------------------------
        // Get a code generator and declare local variables and
        // labels. Save the input array to a local variable.
        //
        ILGenerator ilgen = factory.GetILGenerator();

        LocalBuilder retVal = ilgen.DeclareLocal(TOutput);
        LocalBuilder ic = ilgen.DeclareLocal(icollOfTInput);
        LocalBuilder input = ilgen.DeclareLocal(TInput.MakeArrayType());
        LocalBuilder index = ilgen.DeclareLocal(typeof(int));

        Label enterLoop = ilgen.DefineLabel();
        Label loopAgain = ilgen.DefineLabel();

        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Stloc_S, input);

        // Create an instance of TOutput, using the generic method 
        // overload of the Activator.CreateInstance method. 
        // Using this overload requires the specified type to have
        // a parameterless constructor, which is the reason for adding 
        // that constraint to TOutput. Create the constructed generic
        // method by passing TOutput to MakeGenericMethod. After
        // emitting code to call the method, emit code to store the
        // new TOutput in a local variable. 
        //
        MethodInfo createInst = 
            typeof(Activator).GetMethod("CreateInstance", Type.EmptyTypes);
        MethodInfo createInstOfTOutput = 
            createInst.MakeGenericMethod(TOutput);

        ilgen.Emit(OpCodes.Call, createInstOfTOutput);
        ilgen.Emit(OpCodes.Stloc_S, retVal);

        // Load the reference to the TOutput object, cast it to
        // ICollection<TInput>, and save it.
        //
        ilgen.Emit(OpCodes.Ldloc_S, retVal);
        ilgen.Emit(OpCodes.Box, TOutput);
        ilgen.Emit(OpCodes.Castclass, icollOfTInput);
        ilgen.Emit(OpCodes.Stloc_S, ic);

        // Loop through the array, adding each element to the new
        // instance of TOutput. Note that in order to get a MethodInfo
        // for ICollection<TInput>.Add, it is necessary to first 
        // get the Add method for the generic type defintion,
        // ICollection<T>.Add. This is because it is not possible
        // to call GetMethod on icollOfTInput. The static overload of
        // TypeBuilder.GetMethod produces the correct MethodInfo for
        // the constructed type.
        //
        MethodInfo mAddPrep = icoll.GetMethod("Add");
        MethodInfo mAdd = TypeBuilder.GetMethod(icollOfTInput, mAddPrep);

        // Initialize the count and enter the loop.
        ilgen.Emit(OpCodes.Ldc_I4_0);
        ilgen.Emit(OpCodes.Stloc_S, index);
        ilgen.Emit(OpCodes.Br_S, enterLoop);

        // Mark the beginning of the loop. Push the ICollection
        // reference on the stack, so it will be in position for the
        // call to Add. Then push the array and the index on the 
        // stack, get the array element, and call Add (represented
        // by the MethodInfo mAdd) to add it to the collection.
        //
        // The other ten instructions just increment the index
        // and test for the end of the loop. Note the MarkLabel
        // method, which sets the point in the code where the 
        // loop is entered. (See the earlier Br_S to enterLoop.)
        //
        ilgen.MarkLabel(loopAgain);

        ilgen.Emit(OpCodes.Ldloc_S, ic);
        ilgen.Emit(OpCodes.Ldloc_S, input);
        ilgen.Emit(OpCodes.Ldloc_S, index);
        ilgen.Emit(OpCodes.Ldelem, TInput);
        ilgen.Emit(OpCodes.Callvirt, mAdd);

        ilgen.Emit(OpCodes.Ldloc_S, index);
        ilgen.Emit(OpCodes.Ldc_I4_1);
        ilgen.Emit(OpCodes.Add);
        ilgen.Emit(OpCodes.Stloc_S, index);

        ilgen.MarkLabel(enterLoop);
        ilgen.Emit(OpCodes.Ldloc_S, index);
        ilgen.Emit(OpCodes.Ldloc_S, input);
        ilgen.Emit(OpCodes.Ldlen);
        ilgen.Emit(OpCodes.Conv_I4);
        ilgen.Emit(OpCodes.Clt);
        ilgen.Emit(OpCodes.Brtrue_S, loopAgain);

        ilgen.Emit(OpCodes.Ldloc_S, retVal);
        ilgen.Emit(OpCodes.Ret);

        // Complete the type.
        Type dt = demoType.CreateType();
        // Save the assembly, so it can be examined with Ildasm.exe.
        demoAssembly.Save(asmName.Name+".dll");

        // To create a constructed generic method that can be
        // executed, first call the GetMethod method on the completed 
        // type to get the generic method definition. Call MakeGenericType
        // on the generic method definition to obtain the constructed
        // method, passing in the type arguments. In this case, the
        // constructed method has string for TInput and List<string>
        // for TOutput. 
        //
        MethodInfo m = dt.GetMethod("Factory");
        MethodInfo bound = 
            m.MakeGenericMethod(typeof(string), typeof(List<string>));

        // Display a string representing the bound method.
        Console.WriteLine(bound);


        // Once the generic method is constructed, 
        // you can invoke it and pass in an array of objects 
        // representing the arguments. In this case, there is only
        // one element in that array, the argument 'arr'.
        //
        object o = bound.Invoke(null, new object[]{arr});
        List<string> list2 = (List<string>) o;

        Console.WriteLine("The first element is: {0}", list2[0]);


        // You can get better performance from multiple calls if
        // you bind the constructed method to a delegate. The 
        // following code uses the generic delegate D defined 
        // earlier.
        //
        Type dType = typeof(D<string, List <string>>);
        D<string, List <string>> test;
        test = (D<string, List <string>>) 
            Delegate.CreateDelegate(dType, bound);

        List<string> list3 = test(arr);
        Console.WriteLine("The first element is: {0}", list3[0]);
    }
}

/* This code example produces the following output:

The first element is: a
System.Collections.Generic.List`1[System.String] Factory[String,List`1](System.String[])
The first element is: a
The first element is: a
 */

التحويل البرمجي للتعليمات البرمجية

  • 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 تطبيق وحدة تحكم قالب مشروع.

راجع أيضًا:

المهام

كيفية القيام بما يلي: قم بتعريف نوع عام مع انعكاس حذف

المرجع

MethodBuilder