كيف يمكنني استخدام انعكاس استدعاء خاص الأسلوب ؟

StackOverflow https://stackoverflow.com/questions/135443

سؤال

هناك مجموعة من أساليب خاصة في صفي و لا تحتاج إلى استدعاء واحد حيوي استنادا إلى قيمة المدخلات.كل استدعاء التعليمات البرمجية الهدف الأساليب هي في نفس الدرجة.رمز يبدو مثل هذا:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });

في هذه الحالة ، GetMethod() لن يعود أساليب خاصة.ما BindingFlags هل أنا بحاجة إلى إمدادات GetMethod() بحيث يمكن تحديد موقع خاص الأساليب ؟

هل كانت مفيدة؟

المحلول

ببساطة تغيير التعليمات البرمجية لاستخدام طاقتها نسخة من GetMethod أن يقبل BindingFlags:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

هنا BindingFlags تعداد الوثائق.

نصائح أخرى

BindingFlags.NonPublic لن يعود أي نتائج في حد ذاته.كما اتضح أن الجمع بين ذلك مع BindingFlags.Instance لا حيلة.

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);

وإذا كنت حقا ترغب في الحصول على نفسك في ورطة ، تجعل من السهل لتنفيذ طريق كتابة امتداد الطريقة:

static class AccessExtensions
{
    public static object call(this object o, string methodName, params object[] args)
    {
        var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
        if (mi != null) {
            return mi.Invoke (o, args);
        }
        return null;
    }
}

و الاستخدام:

    class Counter
    {
        public int count { get; private set; }
        void incr(int value) { count += value; }
    }

    [Test]
    public void making_questionable_life_choices()
    {
        Counter c = new Counter ();
        c.call ("incr", 2);             // "incr" is private !
        c.call ("incr", 3);
        Assert.AreEqual (5, c.count);
    }

مايكروسوفت مؤخرا تعديل انعكاس API تقديم معظم هذه الإجابات عفا عليها الزمن.التالية يجب أن تعمل على المنصات الحديثة (بما في ذلك Xamarin.أشكال UWP):

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

أو كامتداد الطريقة:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    var type = typeof(T);
    var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
    return method.Invoke(obj, args);
}

ملاحظة:

  • إذا كان الأسلوب المطلوب في المتفوقة من obj على T عامة يجب أن يكون بوضوح تعيين نوع الفائقة.

  • إذا كان الأسلوب هو غير متزامن يمكنك استخدام await (Task) obj.InvokeMethod(…).

هل أنت متأكد أن هذا لا يمكن القيام به عن طريق الميراث ؟ التأمل هو آخر شيء يجب عليك أن تنظر عند حل المشكلة ، فإنه يجعل إعادة بيع ديون ، فهم التعليمات البرمجية الخاصة بك ، أي التحليل الآلي أكثر صعوبة.

يبدو أنك ينبغي أن يكون مجرد DrawItem1, DrawItem2 ، وما إلى الدرجة التي تتجاوز dynMethod.

انعكاس خاصة على القطاع الخاص أعضاء خطأ

  • انعكاس فواصل نوع الأمان. يمكنك محاولة استدعاء الأسلوب الذي لا موجود (بعد الآن) ، أو مع المعلمات أو مع الكثير من المعلمات أو لا يكفي...أو حتى في أمر خاطئ (هذا واحد من بلدي المفضلة :) ).بالمناسبة نوع الإرجاع يمكن أن تتغير كذلك.
  • انعكاس بطيء.

خاصة أعضاء انعكاس فواصل التغليف مبدأ مما قد يعرض التعليمات البرمجية التالية :

  • زيادة التعقيد من التعليمات البرمجية الخاصة بك لأنه يحتوي على التعامل مع الداخلية سلوك الطبقات.ما هو مخفي يجب أن تبقى مخفية.
  • يجعل التعليمات البرمجية الخاصة بك من السهل كسر كما أنه سيتم تجميع ولكن لن تعمل إذا كانت طريقة تغير اسمها.
  • يجعل رمز خاص من السهل كسر لأنه إذا كان هو خاص وليس المقصود أن يسمى بهذه الطريقة.ربما خاصة طريقة يتوقع بعض الدولة الداخلية قبل أن يطلق.

ما إذا كان يجب أن تفعل ذلك على أي حال ؟

هناك حالات, عندما كنت تعتمد على طرف ثالث أو كنت بحاجة الى بعض api لا يتعرض عليك أن تفعل بعض التفكير.بعض أيضا استخدامه لاختبار بعض الفئات التي يمتلكونها بل أنهم لا يريدون تغيير واجهة الدخول إلى النظام الداخلي أعضاء فقط من أجل الاختبارات.

إذا كنت تفعل ذلك الحق

  • التخفيف من السهل كسر:

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

  • التخفيف من بطء التفكير:

في الإصدارات الأخيرة من .Net Framework CreateDelegate فاز بنسبة 50 MethodInfo الاحتجاج:

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType, 
  BindingFlags.NonPublic | BindingFlags.Instance);

// Here we create a Func that targets the instance of type which has the 
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
                 typeof(Func<TInput, TOutput[]>), this);

draw المكالمات ستكون حوالي 50x أسرع من MethodInfo.Invoke استخدام draw كمعيار Func مثل:

var res = draw(methodParams);

تحقق من هذا آخر من الألغام أن نرى المؤشر على طريقة مختلفة الدعاء

يمكن أن لا يكون مجرد مختلفة التعادل طريقة لكل نوع التي تريد أن ترسم ؟ ثم استدعاء طاقتها التعادل طريقة تمرير في كائن من نوع itemType التي يمكن استخلاصها.

سؤالك لا تجعل من الواضح ما إذا كان itemType حقا يشير إلى كائنات من أنواع مختلفة.

أعتقد أنك يمكن أن تمر عليه BindingFlags.NonPublic حيث ذلك هو GetMethod الأسلوب.

استدعاء أي طريقة على الرغم من مستوى الحماية على وجوه سبيل المثال.استمتع!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
    var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
    MethodInfo method = null;
    var type = obj.GetType();
    while (method == null && type != null)
    {
        method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
        type = type.BaseType;
    }

    return method?.Invoke(obj, methodParams);
}

BindingFlags.NonPublic

قراءة هذا (التكميلي) الجواب (التي هي في بعض الأحيان الجواب) أن نفهم أين يحدث هذا ، لماذا بعض الناس في هذا الموضوع يشكون من أن "فإنه لا يزال لا يعمل"

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

var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );

أنه أعدم ولكن mi == null

واستمرت في السلوك مثل هذا حتى فعلت "إعادة بناء" على جميع المشاريع المشاركة.كنت وحدة اختبار واحد الجمعية في حين انعكاس الأسلوب كان يجلس في الثالثة للجمعية.كان تماما مربكة ولكن أنا استخدم الإطار الحالي إلى اكتشاف طرق و وجدت أن أسلوب خاص حاولت اختبار الوحدة كان الاسم القديم (أنا تسميته).هذا قال لي هذا العجوز الجمعية أو PDB لا يزال هناك حتى لو وحدة اختبار المشروع يبني - لسبب مشروع الاختبارات لم بنيت."بناء" عملت

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top