मैं उस विधि को कैसे खोज सकता हूं जिसे वर्तमान विधि कहा जाता है?


503

C # में लॉग इन करते समय, मैं उस विधि का नाम कैसे सीख सकता हूं जिसे वर्तमान विधि कहा जाता है? मुझे सब पता है System.Reflection.MethodBase.GetCurrentMethod(), लेकिन मैं स्टैक ट्रेस में इसके नीचे एक कदम जाना चाहता हूं। मैंने स्टैक ट्रेस को पार्स करने पर विचार किया है, लेकिन मैं एक क्लीनर को अधिक स्पष्ट तरीके से खोजने की उम्मीद कर रहा हूं, कुछ ऐसा है Assembly.GetCallingAssembly()लेकिन तरीकों के लिए।


22
यदि आप .net 4.5 बीटा + का उपयोग कर रहे हैं, तो आप CallerInformation API का उपयोग कर सकते हैं ।
रोहित शर्मा

5
कॉलर की जानकारी भी बहुत तेज है
कबूतर

4
मैं एक त्वरित बनाया BenchmarkDotNet मुख्य तीन विधियों का बेंचमार्क ( StackTrace, StackFrameऔर CallerMemberName) और पोस्ट एक सार के रूप में परिणाम दूसरों यहाँ देखने के लिए के लिए: gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1f
शॉन विल्सन

जवाबों:


512

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

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace(); 
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

एक लाइन:

(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name

यह रिफ्लेक्शन [C #] का उपयोग करके गेट कॉलिंग विधि से है ।


12
आप केवल पूरे स्टैक के बजाय अपनी ज़रूरत के अनुसार फ्रेम भी बना सकते हैं:
जोएल कोएहॉर्न

187
नया StackFrame (1) .GetMethod ()। नाम;
जोएल कोएहॉर्न 13

12
हालांकि यह पूरी तरह से विश्वसनीय नहीं है। देखते हैं कि यह टिप्पणी में काम करता है या नहीं! कंसोल एप्लिकेशन में निम्नलिखित को आज़माएं और आप देखें कि कंपाइलर ऑप्टिमाइज़ेशन इसे तोड़ते हैं। स्थिर शून्य मुख्य (स्ट्रिंग [] args) {CallIt (); } निजी स्थिर शून्य CallIt () {अंतिम (); } स्थिर शून्य अंतिम () {स्टैकट्रेस ट्रेस = नया स्टैकट्रेस (); स्टैकफ्रेम फ्रेम = ट्रेस.गेटफ्रैम (1); Console.WriteLine ("{0}। {1} ()", frame.GetMethod ()। DeclaringType.FullName, फ़्रेम .GetMethod ()। Name); }
ब्लैकवैप

10
यह काम नहीं करता है जब कंपाइलर इनलाइन या टेल-कॉल विधि का अनुकूलन करता है, जिस स्थिति में स्टैक ढह जाता है और आपको अपेक्षा से अधिक मूल्य मिलेंगे। जब आप केवल डिबग बिल्ड में इसका उपयोग करते हैं, तो यह अच्छी तरह से काम करेगा।
हाबिल

46
अतीत में मैंने जो भी किया है वह संकलक विशेषता जोड़ रहा है [MethodImplAttribute [MethodImplOptions.NoInlining]] विधि से पहले जो स्टैक ट्रेस को देखेगा। यह सुनिश्चित करता है कि कंपाइलर विधि को लाइन नहीं करेगा, और स्टैक ट्रेस में सच्ची कॉलिंग विधि शामिल होगी (मैं ज्यादातर मामलों में पूंछ-पुनरावृत्ति के बारे में चिंतित नहीं हूं।)
जॉर्डन रीजर

363

C # 5 में आप कॉलर जानकारी का उपयोग करके वह जानकारी प्राप्त कर सकते हैं :

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "") 
{ 
    Console.WriteLine(callerName + "called me."); 
} 

तुम भी प्राप्त कर सकते हैं [CallerFilePath]और [CallerLineNumber]


13
नमस्कार, यह C # 5 नहीं है, यह 4.5 में उपलब्ध है।
AFract

35
@ सुरक्षित भाषा (C #) संस्करण .NET संस्करण के समान नहीं हैं।
kwesolowski

6
@stuartd ऐसा प्रतीत होता है कि [CallerTypeName]वर्तमान .Net ढांचे (4.6.2) और कोर CLR से गिरा दिया गया था
Ph0en1x

4
@ Ph0en1x यह कभी भी फ्रेमवर्क में नहीं था, मेरी बात यह थी कि अगर यह होता तो यह आसान होता, जैसे कि एक CallerMember का टाइप नाम कैसे प्राप्त करें
स्टुअर्ट

3
@DiegoDeberdt - मैंने पढ़ा है कि इसके उपयोग में कोई परावर्तन नहीं है क्योंकि यह संकलन के समय में सभी काम करता है। मेरा मानना ​​है कि यह सटीक है जिसे विधि कहा जाता है।
cchamberlain

109

आप कॉलर सूचना और वैकल्पिक मापदंडों का उपयोग कर सकते हैं:

public static string WhoseThere([CallerMemberName] string memberName = "")
{
       return memberName;
}

यह परीक्षण यह दिखाता है:

[Test]
public void Should_get_name_of_calling_method()
{
    var methodName = CachingHelpers.WhoseThere();
    Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}

जबकि StackTrace काफी तेजी से ऊपर काम करता है और ज्यादातर मामलों में एक प्रदर्शन मुद्दा नहीं होगा, जबकि कॉलर सूचना अभी भी बहुत तेज है। 1000 पुनरावृत्तियों के नमूने में, मैंने इसे 40 गुना तेजी से देखा।


केवल .Net 4.5 से ही उपलब्ध है
DerApe

1
ध्यान दें कि यह काम नहीं करता है, अगर कॉल करने वाला एक एग्रीकल्चर पास करता है: CachingHelpers.WhoseThere("wrong name!");==> "wrong name!"क्योंकि CallerMemberNameयह केवल डिफ़ॉल्ट मान का विकल्प है।
ओलिवियर जैकोट-डेसकॉम्ब्स

@ OlivierJacot-Descombes उसी तरह से काम नहीं करता है जिस तरह से एक विस्तार विधि काम नहीं करेगी यदि आप इसके लिए एक पैरामीटर पारित करते हैं। हालांकि आप एक और स्ट्रिंग पैरामीटर का उपयोग कर सकते हैं। यह भी ध्यान दें कि यदि आपने जैसा तर्क दिया, वैसा करने की कोशिश करने पर पुनर्विचार आपको चेतावनी देगा।
कबूतर

1
@dove आप किसी भी स्पष्ट thisपैरामीटर को एक्सटेंशन विधि में पास कर सकते हैं । इसके अलावा, ओलिवियर सही है, आप एक मान पास कर सकते हैं और [CallerMemberName]लागू नहीं किया जाता है; इसके बजाय यह ओवरराइड के रूप में कार्य करता है जहां सामान्य रूप से डिफ़ॉल्ट मान का उपयोग किया जाएगा। तथ्य की बात के रूप में, यदि हम आईएल को देखते हैं, तो हम देख सकते हैं कि परिणामस्वरूप विधि सामान्य रूप से एक [opt]arg के लिए उत्सर्जित होने से अलग नहीं CallerMemberNameहै, इसलिए इंजेक्शन एक CLR व्यवहार है। अंत में, डॉक्स: "द कॉलर इंफो विशेषताएँ [...] डिफ़ॉल्ट मान को प्रभावित करती है जो उस तर्क को छोड़ दिए जाने पर पारित हो जाता है "
शॉन विल्सन

2
यह सही है और asyncअनुकूल है जो StackFrameआपकी मदद नहीं करेगा। इसके अलावा एक भेड़ के बच्चे से बुलाया जा रहा है प्रभावित नहीं करता है।
एरोन

65

2 की एक त्वरित पुनरावृत्ति गति की तुलना के साथ महत्वपूर्ण हिस्सा है।

http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx

संकलन-समय पर कॉलर का निर्धारण करना

static void Log(object message, 
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}

स्टैक का उपयोग करके कॉलर का निर्धारण करना

static void Log(object message)
{
    // frame 1, true for source info
    StackFrame frame = new StackFrame(1, true);
    var method = frame.GetMethod();
    var fileName = frame.GetFileName();
    var lineNumber = frame.GetFileLineNumber();

    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}

2 दृष्टिकोणों की तुलना

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

तो आप देखते हैं, विशेषताओं का उपयोग करना ज्यादा तेज है! वास्तव में लगभग 25x तेज।


यह तरीका बेहतर तरीका लगता है। यह नाममात्र की समस्या के बिना ज़ामरीन में भी काम करता है जो उपलब्ध नहीं है।
लिंडन ह्यूगी

63

हम श्री असद के कोड (वर्तमान में स्वीकृत उत्तर) पर केवल थोड़ा सा फ्रेम कर सकते हैं, जिसमें हमें केवल उस फ्रेम की आवश्यकता होती है, जिसकी हमें पूरी स्टैक के बजाय आवश्यकता होती है:

new StackFrame(1).GetMethod().Name;

यह थोड़ा बेहतर प्रदर्शन कर सकता है, हालांकि सभी संभावना में यह अभी भी उस एकल फ्रेम को बनाने के लिए पूर्ण स्टैक का उपयोग करना है। इसके अलावा, यह अभी भी एक ही चेतावनी है कि एलेक्स लाइमैन ने कहा (ऑप्टिमाइज़र / मूल कोड परिणामों को भ्रष्ट कर सकता है)। अंत में, आप यह जांचना चाहेंगे कि यह सुनिश्चित हो new StackFrame(1)या .GetFrame(1)न लौटे null, क्योंकि संभावना नहीं दिख रही है।

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


1
क्या यह भी संभव है कि new ClassName(…)अशक्त के बराबर है?
प्रदर्शित नाम

1
क्या अच्छा है कि यह भी .NET मानक 2.0 में काम करता है।
srsedate

60

सामान्य तौर पर, आप System.Diagnostics.StackTraceकक्षा का उपयोग कर सकते हैं एक प्राप्त करने के लिए System.Diagnostics.StackFrame, और फिर GetMethod()एक System.Reflection.MethodBaseवस्तु प्राप्त करने के लिए विधि का उपयोग करें । हालाँकि, इस दृष्टिकोण के कुछ कारण हैं :

  1. यह रनटाइम स्टैक का प्रतिनिधित्व करता है - अनुकूलन एक विधि को इनलाइन कर सकता है, और आप स्टैक ट्रेस में उस विधि को नहीं देखेंगे।
  2. यह किसी भी देशी फ्रेम को नहीं दिखाएगा, इसलिए यदि कोई मौका भी है तो आपकी विधि को एक मूल विधि द्वारा बुलाया जा रहा है, तो यह काम नहीं करेगा , और वास्तव में ऐसा करने का वर्तमान में उपलब्ध तरीका नहीं है।

( नोट: मैं अभी फ़रास असद द्वारा दिए गए उत्तर पर विस्तार कर रहा हूं ।)


2
डिबग मोड में ऑप्टिमाइज़ेशन के साथ बंद हो गया, क्या आप देख पाएंगे कि स्टैक ट्रेस में विधि क्या है?
अटैकिंगहोबो

1
@AttackingHobo: हाँ - जब तक कि विधि इनबिल्ड (पर अनुकूलन) या एक देशी फ्रेम नहीं है, आप इसे देखेंगे।
एलेक्स लाइमन

38

.NET 4.5 के अनुसार, आप कॉलर सूचना विशेषता का उपयोग कर सकते हैं :

  • CallerFilePath - स्रोत फ़ाइल जिसे फ़ंक्शन कहा जाता है;
  • CallerLineNumber - कोड की पंक्ति जिसे फ़ंक्शन कहा जाता है;
  • CallerMemberName - वह सदस्य जो फंक्शन को बुलाता है।

    public void WriteLine(
        [CallerFilePath] string callerFilePath = "", 
        [CallerLineNumber] long callerLineNumber = 0,
        [CallerMemberName] string callerMember= "")
    {
        Debug.WriteLine(
            "Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}", 
            callerFilePath,
            callerLineNumber,
            callerMember);
    }

 

यह सुविधा ".NET कोर" और ".NET मानक" में भी मौजूद है।

संदर्भ

  1. Microsoft - कॉलर जानकारी (C #)
  2. माइक्रोसॉफ्ट - CallerFilePathAttributeक्लास
  3. माइक्रोसॉफ्ट - CallerLineNumberAttributeक्लास
  4. माइक्रोसॉफ्ट - CallerMemberNameAttributeक्लास

15

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

PostSharp जैसे पहलू-उन्मुख प्रोग्रामिंग (AOP) पर विचार करें , जो आपके कोड से कॉल किए जाने के बजाय, आपके कोड को संशोधित करता है, और इस प्रकार जानता है कि यह हर समय कहां है।


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

9

जाहिर है कि यह देर से जवाब है, लेकिन मेरे पास एक बेहतर विकल्प है यदि आप .NET 4.5 या अधिक का उपयोग कर सकते हैं:

internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
    Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}

यह "Namespace.ClassName.MethodName" और ": पाठ" के साथ समाप्त होने के बाद वर्तमान दिनांक और समय को प्रिंट करेगा।
नमूना उत्पादन:

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

नमूना उपयोग:

Logger.WriteInformation<MainWindow>("MainWindow initialized");

8
/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
   return GetCallingMethod("GetCallingMethod");
}

/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
   string str = "";
   try
   {
      StackTrace st = new StackTrace();
      StackFrame[] frames = st.GetFrames();
      for (int i = 0; i < st.FrameCount - 1; i++)
      {
         if (frames[i].GetMethod().Name.Equals(MethodAfter))
         {
            if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
            {
               str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
               break;
            }
         }
      }
   }
   catch (Exception) { ; }
   return str;
}

उफ़, मुझे "मेथड आफ्टर" परम को थोड़ा बेहतर बताना चाहिए था। इसलिए यदि आप "लॉग" प्रकार फ़ंक्शन में इस विधि को कॉल कर रहे हैं, तो आप "लॉग" फ़ंक्शन के बाद ही विधि प्राप्त करना चाहेंगे। तो आप GetCallingMethod ("लॉग") को कॉल करेंगे। -चेर्स
फ्लैंडर्स

6

शायद आप कुछ इस तरह की तलाश कर रहे हैं:

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

4
private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

private static Type GetCallingType()
{
  return new StackFrame(2, false).GetMethod().DeclaringType;
}

एक शानदार क्लास यहाँ है: http://www.csharp411.com/c-get-calling-method/


StackFrame विश्वसनीय नहीं है। "2 फ्रेम" ऊपर जाने से आसानी से बहुत अधिक विधि कॉल वापस जा सकते हैं।
user2864740

2

एक और दृष्टिकोण जिसका मैंने उपयोग किया है वह है सवाल में विधि के लिए एक पैरामीटर जोड़ना। उदाहरण के लिए, इसके बजाय void Foo(), उपयोग करें void Foo(string context)। फिर कुछ अद्वितीय स्ट्रिंग में पास करें जो कॉलिंग संदर्भ को इंगित करता है।

यदि आपको केवल विकास के लिए कॉलर / संदर्भ की आवश्यकता है, तो आप paramशिपिंग से पहले हटा सकते हैं ।


2

विधि नाम और वर्ग नाम प्राप्त करने के लिए यह प्रयास करें:

    public static void Call()
    {
        StackTrace stackTrace = new StackTrace();

        var methodName = stackTrace.GetFrame(1).GetMethod();
        var className = methodName.DeclaringType.Name.ToString();

        Console.WriteLine(methodName.Name + "*****" + className );
    }

1
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

मुझे लगता है कि पर्याप्त होगा।



1

हम कॉलर को खोजने के लिए लैम्ब्डा का भी उपयोग कर सकते हैं।

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

public void MethodA()
    {
        /*
         * Method code here
         */
    }

और आप इसे कॉलर ढूंढना चाहते हैं।

1 है । विधि हस्ताक्षर बदलें ताकि हमारे पास टाइप एक्शन का पैरामीटर हो (Func भी चलेगा):

public void MethodA(Action helperAction)
        {
            /*
             * Method code here
             */
        }

। लैम्ब्डा नाम अनियमित रूप से उत्पन्न नहीं होते हैं। नियम लगता है:> <CallerMethodName> __ X जहां CallerMethodName को पिछले फ़ंक्शन द्वारा बदल दिया गया है और X एक इंडेक्स है।

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

। जब हम MethodA को कॉल करते हैं तो Caller Method द्वारा Action / Func पैरामीटर तैयार किया जाता है। उदाहरण:

MethodA(() => {});

। MethodA के अंदर अब हम ऊपर परिभाषित सहायक फ़ंक्शन को कॉल कर सकते हैं और कॉलर विधि के MethodInfo को ढूंढ सकते हैं।

उदाहरण:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);

0

फ़ेरस असद के जवाब की अतिरिक्त जानकारी।

मैंने new StackFrame(1).GetMethod().Name;.net core 2.1 का उपयोग निर्भरता इंजेक्शन के साथ किया है और मुझे 'प्रारंभ' के रूप में कॉलिंग विधि मिल रही है।

मैंने कोशिश की [System.Runtime.CompilerServices.CallerMemberName] string callerName = "" और यह मुझे सही कॉलिंग विधि देता है


-1
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;

1
मैंने डाउनवोट नहीं किया, लेकिन यह नोट करना चाहता था कि कुछ टेक्स्ट को यह समझाने के लिए कि आपने बहुत ही समान जानकारी क्यों पोस्ट की है (वर्षों बाद) प्रश्न का मूल्य बढ़ सकता है और आगे की डाउनवोटिंग से बच सकते हैं।
शॉन विल्सन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.