क्या आप वर्तमान में निष्पादित विधि का नाम खोजने के लिए प्रतिबिंब का उपयोग कर सकते हैं?


202

जैसे शीर्षक कहता है: क्या प्रतिबिंब आपको वर्तमान में निष्पादित विधि का नाम दे सकता है।

हेइज़ेनबर्ग समस्या के कारण मैं अनुमान लगाने में असमर्थ हूं। आप एक विधि को कैसे कहते हैं जो आपको वर्तमान पद्धति को बदलने के बिना बताएगी कि वर्तमान विधि क्या है? लेकिन मुझे उम्मीद है कि कोई मुझे वहां गलत साबित कर सकता है।

अपडेट करें:

  • भाग 2: यह एक संपत्ति के लिए कोड के अंदर भी देखने के लिए इस्तेमाल किया जा सकता है?
  • भाग 3: प्रदर्शन कैसा होगा?

फ़ाइनल रिजल्ट
मैंने मेथडबेस के बारे में सीखा। GetCurrentMethod ()। मैंने यह भी सीखा कि न केवल मैं एक स्टैक ट्रेस बना सकता हूं, मैं केवल सटीक फ्रेम बना सकता हूं जो मुझे चाहिए।

किसी प्रॉपर्टी के अंदर इसका उपयोग करने के लिए, 'सेट_' या 'गेट_' को निकालने के लिए बस .Sststring (4) लें।


जोएल, मैं इसका एक पुराना सवाल जानता हूं, लेकिन एक विधि के सटीक फ्रेम बनाने से आपका क्या मतलब है?
अभिजीत

यह कॉल स्टैक में एक विशिष्ट आइटम को संदर्भित करता है: स्टैक ट्रेस का वह भाग जो मायने रखता है।
जोएल कोएहॉर्न

जवाबों:


119

.NET 4.5 के अनुसार आप [CallerMemberName] का भी उपयोग कर सकते हैं

उदाहरण: एक संपत्ति सेटर (भाग 2 का उत्तर देने के लिए):

    protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
    {
        this.propertyValues[property] = value;
        OnPropertyChanged(property);
    }

    public string SomeProperty
    {
        set { SetProperty(value); }
    }

कंपाइलर कॉलगर्ल्स में मिलान स्ट्रिंग लिटरल्स की आपूर्ति करेगा, इसलिए मूल रूप से कोई प्रदर्शन ओवरहेड नहीं है।


3
यह भी खूब रही! मैं StackFrame(1)लॉगिंग के लिए अन्य उत्तरों में वर्णित विधि का उपयोग कर रहा था , जो तब तक काम करता था जब तक कि जटर ने चीजों को टालना शुरू करने का फैसला नहीं किया। मैं प्रदर्शन कारणों से इनलाइनिंग को रोकने के लिए विशेषता नहीं जोड़ना चाहता था। [CallerMemberName]दृष्टिकोण का उपयोग करके समस्या को ठीक किया गया। धन्यवाद!
ब्रायन रोजर्स


2
इस बात का ध्यान रखें कि StackFrame (1) का उपयोग डिबग मोड में करें। लेकिन जब संकलन करते समय रिलीज़ मोड का उपयोग करते हैं, तो कुछ अनुकूलन हो सकते हैं और स्टैक वह नहीं हो सकता है जो आप अपेक्षा करते हैं।
एक्सल ओ'कोनेल

क्या यह कॉलिंग मेंबर (यानी कोई-कोई सेफ्टी) नहीं लौटेगा, बजाय वर्तमान में निष्पादित विधि के?
लेनार्ट

1
हां, सेटर का आह्वान करने पर कॉल आएगा OnPropertyChanged("SomeProperty")और नहींOnPropertyChanged("SetProperty")
जॉन निल्सन

189

गैर asyncतरीकों के लिए एक का उपयोग कर सकते हैं

System.Reflection.MethodBase.GetCurrentMethod().Name;

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod

कृपया याद रखें कि asyncविधियों के लिए यह "MoveNext" लौटाएगा।


8
ध्यान रखें कि यह हमेशा अपेक्षित परिणाम नहीं देता है। Ie, छोटे तरीकों या गुणों को अक्सर रिलीज़ बिल्ड में इनबिल्ड किया जाता है, इस स्थिति में परिणाम के बजाय कॉलर का विधि नाम होगा।
हाबिल

5
जहां तक ​​मुझे पता है, नहीं। क्योंकि रनटाइम में, MSIL निष्पादन सूचक (यह JITted) से अब उपलब्ध नहीं है। यदि आप विधि का नाम जानते हैं तो भी आप प्रतिबिंब का उपयोग कर सकते हैं। मुद्दा यह है कि जब इनलाइन किया जाता है, तो वर्तमान में निष्पादित विधि अब एक और विधि है (यानी, एक या अधिक स्टैक अप)। दूसरे शब्दों में, विधि गायब हो गई। यहां तक ​​कि अगर आप अपने तरीके को NoInlining के साथ चिह्नित करते हैं, तो अभी भी एक मौका है जो इसे टेल-कॉल ऑप्टिमाइज़ किया गया है, जिस स्थिति में यह भी चला गया है। हालांकि, डिबग बिल्ड में यह काम करेगा।
हाबिल

1
इनलाइन ऐड से बचने के लिए [MethodImpl (MethodImplOptions.NoInlining)] विधि के शीर्ष पर विशेषता।
एलेक्स। सेपर

asyncविधि के अंदर आपको विधि नाम के रूप में "MoveNext" मिलेगा।
विक्टर यारेमा

46

लेक्स द्वारा प्रदान किया गया स्निपेट थोड़ा लंबा था, इसलिए मैं महत्वपूर्ण हिस्से को इंगित कर रहा हूं क्योंकि कोई भी एक ही तकनीक का उपयोग नहीं करता है:

string MethodName = new StackFrame(0).GetMethod().Name;

यह MethodBase.GetCurrentMethod () नाम तकनीक के समान परिणाम लौटाता है , लेकिन यह अभी भी इंगित करने योग्य है क्योंकि मैं इसे पिछली विधि के लिए अनुक्रमणिका 1 का उपयोग करके एक बार अपने तरीके से लागू कर सकता हूं और इसे कई विभिन्न गुणों से कॉल कर सकता हूं । इसके अलावा, यह केवल एक फ्रेम लौटाता है बल्कि पूरे स्टैक ट्रेस:

private string GetPropertyName()
{  //.SubString(4) strips the property prefix (get|set) from the name
    return new StackFrame(1).GetMethod().Name.Substring(4);
}

यह वन-लाइनर भी है;)


सहायक श्रेणी में सार्वजनिक स्थैतिक स्ट्रिंग GetPropertyName () हो सकता है? स्थिर विधि?
कीकनीत

2
एड गुनेस के उत्तर के समान: स्टैक रिलीज़ बिल्ड में भिन्न हो सकते हैं और पहली विधि इनलाइनिंग या टेल कॉल ऑप्टिमाइज़ेशन के मामलों में वर्तमान विधि के समान नहीं हो सकती है।
हाबिल

यदि आप .net 4.5 का उपयोग कर रहे हैं, तो इनलाइनिंग समस्या के आसपास जॉन निल्सन का उत्तर अच्छे तरीके से देखें।
ब्रायन रोजर्स

यह स्वीकृत उत्तर और उपरोक्त उत्तर से भी बेहतर हो सकता है
T.Todua

16

खाली कंसोल प्रोग्राम में मुख्य विधि के अंदर इसे आज़माएँ:

MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);

कंसोल आउटपुट:
Main


12

हाँ बिलकुल।

यदि आप किसी वस्तु को हेरफेर करना चाहते हैं तो मैं वास्तव में इस तरह से एक फ़ंक्शन का उपयोग करता हूं:

public static T CreateWrapper<T>(Exception innerException, params object[] parameterValues) where T : Exception, new()
{
    if (parameterValues == null)
    {
        parameterValues = new object[0];
    }

    Exception exception   = null;
    StringBuilder builder = new StringBuilder();
    MethodBase method     = new StackFrame(2).GetMethod();
    ParameterInfo[] parameters = method.GetParameters();
    builder.AppendFormat(CultureInfo.InvariantCulture, ExceptionFormat, new object[] { method.DeclaringType.Name, method.Name });
    if ((parameters.Length > 0) || (parameterValues.Length > 0))
    {
        builder.Append(GetParameterList(parameters, parameterValues));
    }

    exception = (Exception)Activator.CreateInstance(typeof(T), new object[] { builder.ToString(), innerException });
    return (T)exception;
}

यह रेखा:

MethodBase method     = new StackFrame(2).GetMethod();

कॉलिंग विधि खोजने के लिए स्टैक फ्रेम पर चलता है फिर हम जेनेरिक त्रुटि रिपोर्टिंग फ़ंक्शन के लिए पैरामीटर सूचना मान प्राप्त करने के लिए प्रतिबिंब का उपयोग करते हैं। वर्तमान विधि को प्राप्त करने के लिए बस इसके बजाय वर्तमान स्टैक फ्रेम (1) का उपयोग करें।

जैसा कि अन्य लोगों ने कहा है कि वर्तमान विधियों के नाम के लिए भी आप उपयोग कर सकते हैं:

MethodBase.GetCurrentMethod()

मैं स्टैक घूमना पसंद करता हूं क्योंकि अगर उस तरीके से आंतरिक रूप से देखें तो यह वैसे भी एक StackCrawlMark बनाता है। सीधे तौर पर स्टैक को संबोधित करना मुझे स्पष्ट लगता है

4.5 पद अब आप विधि के नाम की एक स्ट्रिंग प्राप्त करने के लिए विधि पैरामीटर के भाग के रूप में [CallerMemberNameAttribute] का उपयोग कर सकते हैं - यह कुछ परिदृश्यों में मदद कर सकता है (लेकिन वास्तव में ऊपर उदाहरण में कहें)

public void Foo ([CallerMemberName] string methodName = null)

यह मुख्य रूप से INotifyPropertyChanged समर्थन के लिए एक समाधान प्रतीत होता है, जहां पहले आपने अपने ईवेंट कोड के माध्यम से सभी तार बिछाए थे।


मूर्खतापूर्ण नहीं; मैंने बस उन्हें पास कर दिया। आप शायद इसे देखने के लिए और अधिक सरल बनाने के लिए कुछ कर सकते हैं लेकिन अनुपात को पुरस्कृत करने का प्रयास इसे सरल रखने के पक्ष में था। अनिवार्य रूप से देव सिर्फ विधि हस्ताक्षर (पाठ्यक्रम के प्रकारों को हटाने) की पैरामीटर सूची में कॉपी करता है।
लेक्स

यह क्या है: ExceptionFormat और GetParameterList?
किकेनेट

उत्तर में देर से लेकिन लेकिन: ExceptionFormat एक निरंतर स्ट्रिंग प्रारूप है और GetParameterList एक सरल कार्य है जो मानों के साथ मापदंडों को प्रारूपित करता है (आप इनलाइन कर सकते हैं)
Lex

11

विधि नाम प्राप्त करने के तरीकों की तुलना करना - एक मनमाना समय निर्माण का उपयोग करना LinqPad में का उपयोग करना:

कोड

void Main()
{
    // from http://blogs.msdn.com/b/webdevelopertips/archive/2009/06/23/tip-83-did-you-know-you-can-get-the-name-of-the-calling-method-from-the-stack-using-reflection.aspx
    // and /programming/2652460/c-sharp-how-to-get-the-name-of-the-current-method-from-code

    var fn = new methods();

    fn.reflection().Dump("reflection");
    fn.stacktrace().Dump("stacktrace");
    fn.inlineconstant().Dump("inlineconstant");
    fn.constant().Dump("constant");
    fn.expr().Dump("expr");
    fn.exprmember().Dump("exprmember");
    fn.callermember().Dump("callermember");

    new Perf {
        { "reflection", n => fn.reflection() },
        { "stacktrace", n => fn.stacktrace() },
        { "inlineconstant", n => fn.inlineconstant() },
        { "constant", n => fn.constant() },
        { "expr", n => fn.expr() },
        { "exprmember", n => fn.exprmember() },
        { "callermember", n => fn.callermember() },
    }.Vs("Method name retrieval");
}

// Define other methods and classes here
class methods {
    public string reflection() {
        return System.Reflection.MethodBase.GetCurrentMethod().Name;
    }
    public string stacktrace() {
        return new StackTrace().GetFrame(0).GetMethod().Name;
    }
    public string inlineconstant() {
        return "inlineconstant";
    }
    const string CONSTANT_NAME = "constant";
    public string constant() {
        return CONSTANT_NAME;
    }
    public string expr() {
        Expression<Func<methods, string>> ex = e => e.expr();
        return ex.ToString();
    }
    public string exprmember() {
        return expressionName<methods,string>(e => e.exprmember);
    }
    protected string expressionName<T,P>(Expression<Func<T,Func<P>>> action) {
        // https://stackoverflow.com/a/9015598/1037948
        return ((((action.Body as UnaryExpression).Operand as MethodCallExpression).Object as ConstantExpression).Value as MethodInfo).Name;
    }
    public string callermember([CallerMemberName]string name = null) {
        return name;
    }
}

परिणाम

प्रतिबिंब प्रतिबिंब

स्टैकट्रेस स्टैकट्रेस

inlineconstant inlineconstant

निरंतर स्थिर

expr e => e.expr ()

विस्तारक exprmember

कॉलमैम्बर मेन

Method name retrieval: (reflection) vs (stacktrace) vs (inlineconstant) vs (constant) vs (expr) vs (exprmember) vs (callermember) 

 154673 ticks elapsed ( 15.4673 ms) - reflection
2588601 ticks elapsed (258.8601 ms) - stacktrace
   1985 ticks elapsed (  0.1985 ms) - inlineconstant
   1385 ticks elapsed (  0.1385 ms) - constant
1366706 ticks elapsed (136.6706 ms) - expr
 775160 ticks elapsed ( 77.516  ms) - exprmember
   2073 ticks elapsed (  0.2073 ms) - callermember


>> winner: constant

ध्यान दें कि exprऔर callermemberविधियां काफी "सही" नहीं हैं। और वहाँ आप एक संबंधित टिप्पणी की पुनरावृत्ति देखते हैं कि प्रतिबिंब ~ 15x स्टैकट्रेस से अधिक तेज है।


9

संपादित करें: मेथडबेस शायद एक बेहतर तरीका है कि आप जिस पद्धति में हैं उसे प्राप्त करें (पूरे कॉलिंग स्टैक के विपरीत)। मैं अभी भी inlining के बारे में चिंतित हूँ।

आप विधि के भीतर एक StackTrace का उपयोग कर सकते हैं:

StackTrace st = new StackTrace(true);

और तख्ते को देखो:

// The first frame will be the method you want (However, see caution below)
st.GetFrames();

हालाँकि, इस बात से अवगत रहें कि यदि विधि अंतर्निर्मित है, तो आप उस विधि के अंदर नहीं होंगे जो आपको लगता है कि आप हैं। आप इनलाइनिंग को रोकने के लिए एक विशेषता का उपयोग कर सकते हैं:

[MethodImpl(MethodImplOptions.NoInlining)]

रिलीज़ ऑप्टिमाइज़ेशन के कारण इनलाइन विशेष रूप से मुश्किल है क्योंकि कोड डिबग और रिलीज़ कॉन्फ़िगरेशन में अलग-अलग व्यवहार करेगा। छोटे गुणों के लिए देखें, वे इसके सबसे अधिक शिकार हैं।
डी.के.

मुझे आश्चर्य है कि क्यों आप के new StackTrace(true)बजाय जोर से इस्तेमाल करते हैं new StackTrace(false)। सेटिंग जो trueफ़ाइल नाम, लाइन नंबर और आदि कैप्चर करने के लिए स्टैक ट्रेस का कारण बनेगी, जो इस कॉल को धीमा कर सकती है। अन्यथा, एक अच्छा जवाब
Ivaylo Slavov

6

सौदा करने का सरल तरीका है:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;

यदि System.Reflection का उपयोग ब्लॉक में किया जाता है:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;

4

यह इस बारे में कैसे:

StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name

MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name


0

इसे इस्तेमाल करे...

    /// <summary>
    /// Return the full name of method
    /// </summary>
    /// <param name="obj">Class that calls this method (use Report(this))</param>
    /// <returns></returns>
    public string Report(object obj)
    {
        var reflectedType = new StackTrace().GetFrame(1).GetMethod().ReflectedType;
        if (reflectedType == null) return null;

        var i = reflectedType.FullName;
        var ii = new StackTrace().GetFrame(1).GetMethod().Name;

        return string.Concat(i, ".", ii);
    }

0

मैंने इसे एक साधारण स्थिर वर्ग के साथ किया है:

using System.Runtime.CompilerServices;
.
.
.
    public static class MyMethodName
        {
            public static string Show([CallerMemberName] string name = "")
            {
                return name;
            }
        }

फिर आपके कोड में:

private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

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