633

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.

Shaun Wilson
  • 8,727
  • 3
  • 50
  • 48
flipdoubt
  • 13,897
  • 15
  • 64
  • 96
  • 23
    If you are using .net 4.5 beta +, you can use [CallerInformation API](http://msdn.microsoft.com/en-us/library/hh534540%28v=vs.110%29.aspx). – Rohit Sharma Mar 11 '12 at 07:17
  • 6
    Caller Information is also much [faster](http://stackoverflow.com/questions/171970/how-can-i-find-the-method-that-called-the-current-method/13496108#13496108) – dove Nov 21 '12 at 15:08
  • 9
    I created a quick [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) benchmark of the three main methods (`StackTrace`, `StackFrame` and `CallerMemberName`) and posted the results as a gist for others to see here: https://gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1f – Shaun Wilson Mar 17 '18 at 20:02
  • 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. – David Klempfner Jan 19 '21 at 09:56

18 Answers18

651

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#].

T.Todua
  • 53,146
  • 19
  • 236
  • 237
Firas Assaad
  • 25,006
  • 16
  • 61
  • 78
  • That deffo works because thats the solution we used at work...not sure why we did it though! – Surgical Coder Oct 05 '08 at 13:36
  • 15
    You can also create just the frame you need, rather than the entire stack: – Joel Coehoorn Oct 06 '08 at 13:05
  • 242
    new StackFrame(1).GetMethod().Name; – Joel Coehoorn Oct 06 '08 at 13:05
  • 14
    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 Jun 01 '09 at 15:13
  • Ah well, thought the comments may not like it. – BlackWasp Jun 01 '09 at 15:14
  • 12
    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. – Abel Mar 19 '12 at 12:27
  • 50
    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.) – Jordan Rieger Aug 03 '12 at 19:52
  • 2
    But the compiler may inline the calling method. – Mikhail Orlov Feb 08 '16 at 09:48
  • @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. – ebyrob Jun 20 '17 at 15:17
  • Works well. Be wary of unexpected behaviour if using lambdas, described here; https://stackoverflow.com/a/21564897/1146862. – Aaron Mar 06 '19 at 00:43
  • 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](https://stackoverflow.com/a/6585089/1931573) - "The purpose of a call stack is to tell you **where you are going next**, not **where you came from**." – Nick Apr 30 '20 at 22:21
  • 1
    `new StackFrame(1)...` is the best way and is more optimized. – Hugo Leonardo Apr 12 '21 at 13:11
505

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].

Pang
  • 9,564
  • 146
  • 81
  • 122
Coincoin
  • 27,880
  • 7
  • 55
  • 76
  • 15
    Hello, it's not C# 5, it is available in 4.5. – AFract Feb 16 '15 at 14:06
  • 60
    @AFract Language (C#) versions are not the same as .NET version. – kwesolowski Feb 22 '15 at 21:22
  • 7
    @stuartd Looks like `[CallerTypeName]` was dropped from current .Net framework (4.6.2) and Core CLR – Ph0en1x Apr 13 '16 at 12:30
  • 4
    @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](http://stackoverflow.com/questions/17893827/c-sharp-how-to-get-type-name-of-a-callermember) – stuartd Apr 13 '16 at 14:24
  • 1
    Does this solution have the same issues with compiler optimizations as the StackTrace solution ? – Captain Sensible Sep 02 '16 at 09:31
  • 4
    @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. – cchamberlain May 28 '17 at 08:46
  • It work for me even I using `StartCoroutine`, `IEnumerator` in Unity. Thank you. – CWKSC Apr 17 '20 at 16:07
  • Is there a way to get the caller's class name using the same approach? – Grigory Zhadko Sep 24 '21 at 06:18
  • 1
    What If I want to use this on a method that is an overridden version of something? Than I cant have an extra parameter, like [CallerMemberName] – dezox Jan 12 '23 at 11:27
  • @Ph0en1x the correct attribute is `CallerMemberName` which has been in the .NET stack since .NET Framework 4.5 - https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callermembernameattribute – Dave Black Feb 06 '23 at 17:15
128

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.

AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152
dove
  • 20,469
  • 14
  • 82
  • 108
  • 2
    Only available from .Net 4.5 though – DerApe Jul 06 '15 at 10:41
  • 1
    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. – Olivier Jacot-Descombes Dec 06 '17 at 16:09
  • @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. – dove Dec 06 '17 at 16:30
  • 1
    @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 Mar 17 '18 at 18:35
  • 4
    This is perfect and is `async` friendly which `StackFrame` won't help you with. Also doesn't affect being called from a lambda. – Aaron Mar 06 '19 at 01:25
  • Apparently this cannot be used with a trailing params object as in: GetMethodAndInitialParameterValues([CallerMemberName] string memberName = null, params object[] parameterValues) { } – newby Jun 18 '20 at 15:35
  • Is there a way to get the caller's class name using the same approach? – Grigory Zhadko Sep 24 '21 at 06:18
  • @GrigoryZhadko [CallerFilePath] should give you that – dove Sep 24 '21 at 09:38
98

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.

Tikall
  • 2,453
  • 25
  • 13
  • 1
    This method seem to be superior approach. It also works in Xamarin without a problem of namespaces not being available. – lyndon hughey Sep 19 '17 at 14:18
70

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?

Community
  • 1
  • 1
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
61

In general, you can use the System.Diagnostics.StackTrace class to get a System.Diagnostics.StackFrame, and then use the GetMethod() method to get a System.Reflection.MethodBase object. However, there are some caveats to this approach:

  1. It represents the runtime stack -- optimizations could inline a method, and you will not see that method in the stack trace.
  2. It will not show any native frames, so if there's even a chance your method is being called by a native method, this will not work, and there is in-fact no currently available way to do it.

(NOTE: I am just expanding on the answer provided by Firas Assad.)

Community
  • 1
  • 1
Alex Lyman
  • 15,637
  • 3
  • 38
  • 42
53

As of .NET 4.5 you can use Caller Information Attributes:

  • CallerFilePath - The source file that called the function;
  • CallerLineNumber - Line of code that called the function;
  • CallerMemberName - Member that called the function.

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

 

This facility is also present in ".NET Core" and ".NET Standard".

References

  1. Microsoft - Caller Information (C#)
  2. Microsoft - CallerFilePathAttribute Class
  3. Microsoft - CallerLineNumberAttribute Class
  4. Microsoft - CallerMemberNameAttribute Class
Shaun Wilson
  • 8,727
  • 3
  • 50
  • 48
Ivan Pinto
  • 531
  • 4
  • 3
19

Obviously this is a late answer, but I have a better option if you can use .NET 4.5 or newer:

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

This will print the current Date and Time, followed by "Namespace.ClassName.MethodName" and ending with ": text".
Sample output:

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

Sample use:

Logger.WriteInformation<MainWindow>("MainWindow initialized");
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
  • Great answer. It sucks that the `CallerMemberName` has to be defined as a parameter, though. It's often common to pass in a params array as the final parameter of a log method, which makes it difficult to combine these two things. – devklick Jun 18 '23 at 11:00
14

Note that doing so will be unreliable in release code, due to optimization. Additionally, running the application in sandbox mode (network share) won't allow you to grab the stack frame at all.

Consider aspect-oriented programming (AOP), like PostSharp, which instead of being called from your code, modifies your code, and thus knows where it is at all times.

guneysus
  • 6,203
  • 2
  • 45
  • 47
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 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 Jun 20 '17 at 14:56
9
/// <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;
}
Flanders
  • 91
  • 1
  • 1
  • 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 – Flanders May 28 '10 at 20:10
7

Maybe you are looking for something like this:

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
jesal
  • 7,852
  • 6
  • 50
  • 56
4
private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

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

A fantastic class is here: http://www.csharp411.com/c-get-calling-method/

Orson
  • 14,981
  • 11
  • 56
  • 70
3

Another approach I have used is to add a parameter to the method in question. For example, instead of void Foo(), use void Foo(string context). Then pass in some unique string that indicates the calling context.

If you only need the caller/context for development, you can remove the param before shipping.

Irshad
  • 3,071
  • 5
  • 30
  • 51
GregUzelac
  • 462
  • 2
  • 6
3

For getting Method Name and Class Name try this:

    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 );
    }
Arian
  • 12,793
  • 66
  • 176
  • 300
3

Extra information to Firas Assaad answer.

I have used new StackFrame(1).GetMethod().Name; in .net core 2.1 with dependency injection and I am getting calling method as 'Start'.

I tried with [System.Runtime.CompilerServices.CallerMemberName] string callerName = "" and it gives me correct calling method

cdev
  • 5,043
  • 2
  • 33
  • 32
1

We can also use lambda's in order to find the caller.

Suppose you have a method defined by you:

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

and you want to find it's caller.

1. Change the method signature so we have a parameter of type Action (Func will also work):

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

2. Lambda names are not generated randomly. The rule seems to be: > <CallerMethodName>__X where CallerMethodName is replaced by the previous function and X is an index.

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

3. When we call MethodA the Action/Func parameter has to be generated by the caller method. Example:

MethodA(() => {});

4. Inside MethodA we can now call the helper function defined above and find the MethodInfo of the caller method.

Example:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
Kraang Prime
  • 9,981
  • 10
  • 58
  • 124
smiron
  • 408
  • 3
  • 13
0
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

will be enough, I think.

caner
  • 158
  • 8
0
public static string GetCallerFullName<T>(this T callingObject,[CallerMemberName] string caller = default) =>$"{callingObject.GetType().Name}:{caller}";

This is a slight improvement to Camilo Terevinto's solution because the calling convention is bit cleaner

this.GetCallerFullName();
Ronijo
  • 71
  • 1
  • 8