मैं एक निजी विधि को लागू करने के लिए प्रतिबिंब का उपयोग कैसे करूं?


326

मेरी कक्षा में निजी विधियों का एक समूह है, और मुझे एक इनपुट मूल्य के आधार पर गतिशील रूप से कॉल करने की आवश्यकता है। दोनों कोड और लक्ष्य विधियाँ एक ही उदाहरण में हैं। कोड इस तरह दिखता है:

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

इस मामले में, GetMethod()निजी तरीके नहीं लौटाएंगे। मुझे क्या BindingFlagsआपूर्ति करने की आवश्यकता है GetMethod()ताकि वह निजी तरीकों का पता लगा सके?

जवाबों:


498

BindingFlags के ओवरलोड संस्करण काGetMethod उपयोग करने के लिए बस अपना कोड बदलें :

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

यहाँ BindingFlags गणन प्रलेखन है


248
मैं खुद को इससे बहुत परेशान करने जा रहा हूं।
फ्रैंक Schwieterman

1
BindingFlags.NonPublicवापस नहीं आने की privateविधि .. :(
मोइमित

4
@MoumitMondal क्या आपके तरीके स्थिर हैं? आपको गैर-स्थिर विधियों के लिए BindingFlags.Instanceभी निर्दिष्ट करना होगा BindingFlags.NonPublic
ब्रायनएस

नहीं @BrianS .. विधि है non-staticऔर privateवर्ग से विरासत में मिला है System.Web.UI.Page.. यह मुझे वैसे भी बेवकूफ बनाता है .. मुझे इसका कारण नहीं मिला .. :(
Moumit

3
जोड़ने BindingFlags.FlattenHierarchyसे आप अपने उदाहरण के लिए मूल कक्षाओं से विधियाँ प्राप्त कर सकेंगे।
ड्रैगन्थफौट्स

67

BindingFlags.NonPublicअपने आप कोई परिणाम नहीं लौटाएगा। जैसा कि यह पता चला है, इसके साथ संयोजन BindingFlags.Instanceचाल करता है।

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

एक ही तर्क के internalरूप में अच्छी तरह से कार्य करता है
supertopi

मुझे एक ऐसी ही समस्या है। क्या होगा यदि "यह" एक बाल वर्ग है और आप माता-पिता की निजी पद्धति को लागू करने का प्रयास करते हैं?
persianLife

क्या बेस.बेस क्लास संरक्षित विधियों को लागू करने के लिए इसका उपयोग करना संभव है?
शिव

51

और अगर आप वास्तव में खुद को परेशानी में डालना चाहते हैं, तो विस्तार विधि लिखकर निष्पादित करना आसान बनाएं:

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);
    }

14
खतरनाक? हाँ। लेकिन जब मेरी यूनिट टेस्टिंग नेमस्पेस में लिपटा है तो एक बड़ा सहायक विस्तार। इसके लिए धन्यवाद।
रॉबर्ट वेहलर

5
यदि आप वास्तविक अपवादों के बारे में परवाह करते हैं, तो विधि से इसे फेंक दिया जाता है, यह एक अच्छा विचार है कि इसे पकड़ने की कोशिश में लपेटें और इसके बजाय आंतरिक अपवाद को फिर से फेंक दें जब TargetInvokationException पकड़ा गया। मैं अपने यूनिट टेस्ट हेल्पर एक्सटेंशन में करता हूं।
स्लोबोदान सावकोविक

2
प्रतिबिंब खतरनाक? हम्म ... सी #, जावा, पायथन ... वास्तव में सब कुछ खतरनाक है, यहां तक ​​कि दुनिया भी: डी आपको बस इस बारे में ध्यान रखना है कि इसे सुरक्षित तरीके से कैसे किया जाए ...
महापुरूष

16

Microsoft ने हाल ही में प्रतिबिंब एपीआई को संशोधित करते हुए इनमें से अधिकांश उत्तरों को अप्रचलित किया है। निम्नलिखित को आधुनिक प्लेटफार्मों (Xamarin.Forms और 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(…)


यह UWP .net संस्करण के लिए कम से कम काम नहीं करता है क्योंकि यह केवल सार्वजनिक विधियों के लिए काम करता है : " एक संग्रह लौटाता है जिसमें मौजूदा प्रकार पर घोषित सभी सार्वजनिक विधियाँ होती हैं जो निर्दिष्ट नाम से मेल खाती हैं "।
Dmytro Bondarenko

1
@DmytroBondarenko मैंने इसे निजी तरीकों के खिलाफ परीक्षण किया और इसने काम किया। मैंने हालांकि, यह देखा। यकीन नहीं है कि यह प्रलेखन से अलग क्यों व्यवहार करता है, लेकिन कम से कम यह काम करता है।
ओवेन जेम्स

हां, मैं अन्य सभी उत्तरों को अप्रचलित नहीं कहूंगा, यदि प्रलेखन का कहना GetDeclareMethod()है कि इसका उपयोग केवल एक सार्वजनिक पद्धति को पुनः प्राप्त करने के लिए किया जाना है।
मास डॉट नेट

10

क्या आप पूरी तरह सुनिश्चित हैं कि यह विरासत के माध्यम से नहीं किया जा सकता है? परावर्तन बहुत आखिरी चीज है जिसे आपको किसी समस्या को हल करते समय देखना चाहिए, यह रिफैक्टिंग, आपके कोड को समझना, और किसी भी स्वचालित विश्लेषण को बहुत कठिन बनाता है।

ऐसा लगता है कि आपके पास सिर्फ एक ड्राय इट 1, ड्रा इट 2, आदि वर्ग होना चाहिए जो आपके dynMethod को ओवरराइड करता है।


1
@ नील के: अन्य परिस्थितियों को देखते हुए, हमने इसके लिए विरासत का उपयोग नहीं करने का निर्णय लिया, इसलिए प्रतिबिंब का उपयोग। ज्यादातर मामलों के लिए, हम इसे इस तरह से करेंगे।
जेरोमे इरविन

8

खासकर निजी सदस्यों पर चिंतन गलत है

  • प्रतिबिंब प्रकार सुरक्षा को तोड़ता है। आप एक ऐसी विधि को लागू करने की कोशिश कर सकते हैं जो मौजूद नहीं है (अब), या गलत मापदंडों के साथ, या बहुत अधिक मापदंडों के साथ, या पर्याप्त नहीं ... या गलत क्रम में भी (यह मेरा पसंदीदा :))। वैसे वापसी का प्रकार भी बदल सकता है।
  • परावर्तन धीमा है।

निजी सदस्य प्रतिबिंब एनकैप्सुलेशन सिद्धांत को तोड़ते हैं और इस प्रकार अपने कोड को निम्न में उजागर करते हैं:

  • अपने कोड की जटिलता बढ़ाएं क्योंकि इसमें कक्षाओं के आंतरिक व्यवहार को संभालना है। जो छिपा है, वह छिपा रहना चाहिए।
  • अपने कोड को तोड़ने के लिए आसान बनाता है क्योंकि यह संकलन करेगा, लेकिन अगर इसका नाम बदल नहीं गया तो यह नहीं चलेगा।
  • निजी कोड को तोड़ने के लिए आसान बनाता है क्योंकि अगर यह निजी है तो इसे उस तरह से बुलाया जाना नहीं है। हो सकता है कि निजी पद्धति कुछ आंतरिक अवस्था की अपेक्षा करती है जिसे कहा जाता है।

क्या होगा अगर मैं इसे वैसे भी करना चाहिए?

ऐसे मामले हैं, जब आप किसी तीसरे पक्ष पर निर्भर होते हैं या आपको कुछ एपीआई उजागर नहीं करने की आवश्यकता होती है, तो आपको कुछ प्रतिबिंब बनाने होंगे। कुछ इसका इस्तेमाल कुछ वर्गों के परीक्षण के लिए भी करते हैं, लेकिन वे केवल आंतरिक सदस्यों को परीक्षण के लिए पहुँच देने के लिए इंटरफ़ेस को बदलना नहीं चाहते हैं।

यदि आप इसे करते हैं, तो इसे सही करें

  • तोड़ने के लिए आसान मिटेट्रेट करें:

आसान ब्रेक इश्यू को कम करने के लिए, यूनिट परीक्षणों में परीक्षण द्वारा किसी भी संभावित ब्रेक का पता लगाना सबसे अच्छा है जो एक निरंतर एकीकरण बिल्ड या इस तरह से चलेगा। बेशक, इसका मतलब है कि आप हमेशा एक ही विधानसभा (जिसमें निजी सदस्य होते हैं) का उपयोग करते हैं। यदि आप एक गतिशील लोड और प्रतिबिंब का उपयोग करते हैं, तो आप आग से खेलना पसंद करते हैं, लेकिन आप हमेशा उस अपवाद को पकड़ सकते हैं जो कॉल का उत्पादन कर सकता है।

  • प्रतिबिंब की सुस्ती को कम करें:

.Net फ्रेमवर्क के हाल के संस्करणों में, 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मानक के रूप में MethodInfo.Invoke उपयोग की तुलना में कॉल लगभग 50x तेज होगी :drawFunc

var res = draw(methodParams);

विभिन्न विधि चालान पर बेंचमार्क देखने के लिए मेरी इस पोस्ट को देखें


1
हालाँकि, मुझे निर्भरता इंजेक्शन यूनिट-परीक्षण का एक पसंदीदा तरीका होना चाहिए, लेकिन मुझे लगता है कि सावधानी के साथ इस्तेमाल किया इकाइयों का उपयोग करने के लिए प्रतिबिंब का उपयोग करने के लिए पूरी तरह से बुराई नहीं है जो अन्यथा परीक्षण के लिए दुर्गम होगा। व्यक्तिगत रूप से मुझे लगता है कि सामान्य [सार्वजनिक] [संरक्षित] [निजी] संशोधक भी हमारे पास होने चाहिए [टेस्ट] [रचना] संशोधक ताकि इन चरणों के दौरान कुछ चीजें दिखाई देना संभव है बिना सब कुछ पूर्ण सार्वजनिक किए और इसलिए उन तरीकों को पूरी तरह से दस्तावेज करना है)
andrew pate

1
निजी सदस्यों पर प्रतिबिंबित करने की समस्याओं को सूचीबद्ध करने के लिए धन्यवाद फैब, इसने मुझे यह समीक्षा करने के लिए प्रेरित किया है कि मैं इसका उपयोग करने के बारे में कैसा महसूस करता हूं और एक निष्कर्ष आया ... इकाई-परीक्षण के लिए प्रतिबिंब का उपयोग करना आपके निजी सदस्यों के लिए गलत है, हालांकि कोड-पाथ को छोड़ दिया गया है वास्तव में गलत है।
andrew pate

2
लेकिन जब यह आम तौर पर नहीं-इकाई-परीक्षण योग्य विरासत कोड के इकाई परीक्षण की बात आती है, तो यह करने के लिए शानदार तरीका है
टीएस

2

क्या आपके पास प्रत्येक प्रकार के लिए एक अलग ड्रा विधि नहीं हो सकती है जिसे आप ड्रा करना चाहते हैं? फिर टाइप किए जाने वाले आइटम टाइप के ऑब्जेक्ट में गुजर रहे ओवरलोडेड ड्रा विधि को कॉल करें।

आपका प्रश्न यह स्पष्ट नहीं करता है कि क्या आइटम टाइप वास्तव में विभिन्न प्रकार की वस्तुओं को संदर्भित करता है।



1

ऑब्जेक्ट इंस्टेंस पर इसके सुरक्षा स्तर के बावजूद किसी भी विधि को आमंत्रित करता है। का आनंद लें!

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);
}

0

यह समझने के लिए कि यह कहाँ जा रहा है और क्यों इस सूत्र में कुछ लोग शिकायत करते हैं कि "यह अभी भी काम नहीं कर रहा है" (पूरक) उत्तर (यह कभी-कभी उत्तर है) पढ़ें।

मैंने यहां एक उत्तर के रूप में एक ही कोड लिखा था । लेकिन मैं अभी भी एक मुद्दा था। मैंने ब्रेक पॉइंट ऑन रखा

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

इसे अंजाम दिया लेकिन mi == null

और यह इस तरह से व्यवहार जारी रखा जब तक कि मैंने शामिल सभी परियोजनाओं पर "फिर से निर्माण" नहीं किया। मैं एक विधानसभा का परीक्षण कर रहा था, जबकि तीसरी विधानसभा में परावर्तन विधि बैठ रही थी। यह पूरी तरह से भ्रमित करने वाला था, लेकिन मैंने तरीकों की खोज करने के लिए तत्काल विंडो का उपयोग किया और मैंने पाया कि मैंने जिस निजी पद्धति से इकाई परीक्षण की कोशिश की उसका पुराना नाम था (मैंने इसका नाम बदल दिया)। इसने मुझे बताया कि पुरानी विधानसभा या पीडीबी अभी भी बाहर है, भले ही इकाई परीक्षण परियोजना का निर्माण करती है - किसी कारण से परियोजना के लिए यह परीक्षण नहीं बनाया गया था। "पुनर्निर्माण" काम किया


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.