How can I find the method that called the current method?

304,719

Solution 1

Try this:

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

one-liner:

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

It is from Get Calling Method using Reflection [C#].

Solution 2

In C# 5, you can get that information using caller info:

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

You can also get the [CallerFilePath] and [CallerLineNumber].

Solution 3

You can use Caller Information and optional parameters:

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

This test illustrates this:

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

While the StackTrace works quite fast above and would not be a performance issue in most cases the Caller Information is much faster still. In a sample of 1000 iterations, I clocked it as 40 times faster.

Solution 4

A quick recap of the 2 approaches with speed comparison being the important part.

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

Determining the caller at compile-time

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

Determining the caller using the stack

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

Comparison of the 2 approaches

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

So you see, using the attributes is much, much faster! Nearly 25x faster in fact.

Solution 5

We can improve on Mr Assad's code (the current accepted answer) just a little bit by instantiating only the frame we actually need rather than the entire stack:

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

This might perform a little better, though in all likelihood it still has to use the full stack to create that single frame. Also, it still has the same caveats that Alex Lyman pointed out (optimizer/native code might corrupt the results). Finally, you might want to check to be sure that new StackFrame(1) or .GetFrame(1) don't return null, as unlikely as that possibility might seem.

See this related question: Can you use reflection to find the name of the currently executing method?

Share:
304,719
flipdoubt
Author by

flipdoubt

Full-stack .NET and Javascript generalist by necessity.

Updated on July 08, 2022

Comments

  • flipdoubt
    flipdoubt almost 2 years

    When logging in C#, how can I learn the name of the method that called the current method? I know all about System.Reflection.MethodBase.GetCurrentMethod(), but I want to go one step beneath this in the stack trace. I've considered parsing the stack trace, but I am hoping to find a cleaner more explicit way, something like Assembly.GetCallingAssembly() but for methods.

    • Rohit Sharma
      Rohit Sharma about 12 years
      If you are using .net 4.5 beta +, you can use CallerInformation API.
    • dove
      dove over 11 years
      Caller Information is also much faster
    • Shaun Wilson
      Shaun Wilson about 6 years
      I created a quick BenchmarkDotNet benchmark of the three main methods (StackTrace, StackFrame and CallerMemberName) and posted the results as a gist for others to see here: gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1f
    • David Klempfner
      David Klempfner about 3 years
      Just in case you want to find where your method is called without running it, keep in mind Shift+F12 doesn't work if the method is called via Reflection. Sometimes you have to use Ctrl+F to search for the method name string.
  • Beverly Guillermo
    Beverly Guillermo over 15 years
    That deffo works because thats the solution we used at work...not sure why we did it though!
  • Joel Coehoorn
    Joel Coehoorn over 15 years
    You can also create just the frame you need, rather than the entire stack:
  • Joel Coehoorn
    Joel Coehoorn over 15 years
    new StackFrame(1).GetMethod().Name;
  • BlackWasp
    BlackWasp almost 15 years
    This isn't entirely reliable though. Let's see if this works in a comment! Try the following in a console application and you see that compiler optimsations break it. static void Main(string[] args) { CallIt(); } private static void CallIt() { Final(); } static void Final() { StackTrace trace = new StackTrace(); StackFrame frame = trace.GetFrame(1); Console.WriteLine("{0}.{1}()", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name); }
  • BlackWasp
    BlackWasp almost 15 years
    Ah well, thought the comments may not like it.
  • Flanders
    Flanders almost 14 years
    oops, I should have explained the "MethodAfter" param a little better. So if you are calling this method in a "log" type function, you'll want to get the method just after the "log" function. so you would call GetCallingMethod("log"). -Cheers
  • AttackingHobo
    AttackingHobo about 13 years
    In debug mode with optimizations turned off, would you be able to see what the method is in the stack trace?
  • Alex Lyman
    Alex Lyman about 13 years
    @AttackingHobo: Yes--unless the method is inlined (optimizations on) or a native frame, you'll see it.
  • Aisah Hamzah
    Aisah Hamzah about 12 years
    This doesn't work when the compiler inlines or tail-call optimizes the method, in which case the stack is collapsed and you will find other values than expected. When you only use this in Debug builds, it'll work well though.
  • Jordan Rieger
    Jordan Rieger over 11 years
    What I have done in the past is add the compiler attribute [MethodImplAttribute(MethodImplOptions.NoInlining)] before the method that will look up the stack trace. That ensures that the compiler will not in-line the method, and the stack trace will contain the true calling method (I'm not worried about tail-recursion in most cases.)
  • Peter Mortensen
    Peter Mortensen over 10 years
    A summary of the content would be nice.
  • AFract
    AFract about 9 years
    Hello, it's not C# 5, it is available in 4.5.
  • kwesolowski
    kwesolowski about 9 years
    @AFract Language (C#) versions are not the same as .NET version.
  • Display Name
    Display Name about 9 years
    is it even possible that new ClassName(…) equals to null?
  • DerApe
    DerApe almost 9 years
    Only available from .Net 4.5 though
  • Mikhail Orlov
    Mikhail Orlov about 8 years
    But the compiler may inline the calling method.
  • Ph0en1x
    Ph0en1x about 8 years
    @stuartd Looks like [CallerTypeName] was dropped from current .Net framework (4.6.2) and Core CLR
  • stuartd
    stuartd about 8 years
    @Ph0en1x it was never in the framework, my point was it would be handy if it was, eg how to get Type name of a CallerMember
  • Captain Sensible
    Captain Sensible over 7 years
    Does this solution have the same issues with compiler optimizations as the StackTrace solution ?
  • user2864740
    user2864740 over 7 years
    StackFrame is not reliable. Going up "2 frames" might easily go back too method calls.
  • cchamberlain
    cchamberlain almost 7 years
    @DiegoDeberdt - I've read that using this has no reflection downsides since it does all the work at compile time. I believe it's accurate as to what called the method.
  • ebyrob
    ebyrob almost 7 years
    You're absolutely correct that this won't work in release. I'm not sure I like the idea of code injection, but I guess in a sense a debug statement requires code modification, but still. Why not just go back to C macros? It's at least something you can see.
  • ebyrob
    ebyrob almost 7 years
    @MikhailOrlov I think if the caller's OWN CODE gets optimized out, then they should maybe be expecting some confusion here? At least at that point they're not troubleshooting the guts of someone else's library.
  • lyndon hughey
    lyndon hughey over 6 years
    This method seem to be superior approach. It also works in Xamarin without a problem of namespaces not being available.
  • srbrills
    srbrills over 6 years
    What's nice is that this also works in .NET Standard 2.0.
  • Olivier Jacot-Descombes
    Olivier Jacot-Descombes over 6 years
    Note that this does not work, if the caller passes an agrument: CachingHelpers.WhoseThere("wrong name!"); ==> "wrong name!" because the CallerMemberName is only substitutes the default value.
  • dove
    dove over 6 years
    @OlivierJacot-Descombes is does not work in that way in the same way an extension method would not work if you passed a parameter to it. you could though another string parameter which could be used. Also note that resharper would give you a warning if you tried to passed an argument like you did.
  • Shaun Wilson
    Shaun Wilson about 6 years
    @dove you can pass any explicit this parameter into an extension method. Also, Olivier is correct, you can pass a value and [CallerMemberName] is not applied; instead it functions as an override where the default value would normally be used. As a matter of fact, if we look at the IL we can see that the resulting method is no different than what would normally have been emitted for an [opt] arg, the injection of CallerMemberName is therefore a CLR behavior. Lastly, the docs: "The Caller Info attributes [...] affect the default value that's passed in when the argument is omitted"
  • Shaun Wilson
    Shaun Wilson about 6 years
    i did not downvote, but wanted to note that adding some text to explain why you posted very similar information (years later) may increase the value of the question and avoid further downvoting.
  • Aaron
    Aaron about 5 years
    Works well. Be wary of unexpected behaviour if using lambdas, described here; stackoverflow.com/a/21564897/1146862.
  • Aaron
    Aaron about 5 years
    This is perfect and is async friendly which StackFrame won't help you with. Also doesn't affect being called from a lambda.
  • CWKSC
    CWKSC about 4 years
    It work for me even I using StartCoroutine, IEnumerator in Unity. Thank you.
  • Nick
    Nick almost 4 years
    Just a warning: This looks like it works and it will most of the time, but some day it will suddenly start returning bad data without warning. Maybe a new compiler version, maybe a new language feature, maybe a new optimization, etc. Like Eric said in a different answer - "The purpose of a call stack is to tell you where you are going next, not where you came from."
  • newby
    newby almost 4 years
    Apparently this cannot be used with a trailing params object as in: GetMethodAndInitialParameterValues([CallerMemberName] string memberName = null, params object[] parameterValues) { }
  • Hugo Leonardo
    Hugo Leonardo about 3 years
    new StackFrame(1)... is the best way and is more optimized.
  • Grigory Zhadko
    Grigory Zhadko over 2 years
    Is there a way to get the caller's class name using the same approach?
  • Grigory Zhadko
    Grigory Zhadko over 2 years
    Is there a way to get the caller's class name using the same approach?
  • dove
    dove over 2 years
    @GrigoryZhadko [CallerFilePath] should give you that