Get current method name from async function?
Is there anyway to get the current method name from inside an async function?
I've tried:
System.Reflection.MethodInfo.GetCurrentMethod();
And I've tried using StackTrace and StrackFrame as follows:
StackTrace strackTrace = new StackTrace();
for (int i = 0; i < strackTrace.GetFrames().Length; i++)
{
SafeNativeMethods.EtwTraceInfo("Function" + i + ":" + function);
SafeNativeMethods.EtwTraceInfo("Type" + i + ":" + strackTrace.GetFrame(i).GetType().Name);
SafeNativeMethods.EtwTraceInfo("Method" + i + ":" + strackTrace.GetFrame(i).GetMethod().Name);
SafeNativeMethods.EtwTraceInfo("ToString Call" + i + ":" + strackTrace.GetFrame(i).ToString());
}
But neither of them seem to work, I'd get ".ctor", "InvokeMethod", "Invoke", "CreateInstance", "CreateKnownObject" or "CreateUnknownObject" or "MoveNext"
Any ideas on how I can do this? I want to create a generic logger function and I don't want to pass in the name of the function that called the logger function, so I tried the stacktrace method, didn't work.
I gave up on that and said, ok, I'll pass in the function name as the first parameter, but when I called the reflection method from the calling function that calls the generic logger function, I always get ".ctor"
Any ideas? Note the generic logger function I'm calling is a static method in the same class (it has to be this way for now...).
C# 5 added caller info attributes which may give you more what you are looking for. Note that these insert the appropriate information into the call site at compile-time rather than using run-time information. The functionality is more limited (you can't get a complete call stack, obviously), but it is much faster.
An example using CallerMemberNameAttribute:
using System.Runtime.CompilerServices;
public static void Main(string[] args)
{
Test().Wait();
}
private static async Task Test()
{
await Task.Yield();
Log();
await Task.Yield();
}
private static void Log([CallerMemberName]string name = "")
{
Console.WriteLine("Log: {0}", name);
}
There are also CallerFilePath and CallerLineNumber attributes which can get other pieces of info about the call site.
This method works from async method call as well as from normal method. (C#5)
/// <summary>
/// Returns Current method name
/// </summary>
/// <returns>callers method name</returns>
public string GetCurrentMethod([CallerMemberName] string callerName = "")
{
return callerName;
}
Rather than doing manual stack frame walks, which is both expensive and risky (since Release builds might optimize some methods out), you can use the CallerMemberNameAttribute
, one of the Caller Information attributes, which was added in .NET 4.5 (which you already use, if you use async/await) for this exact scenario - passing in a member name for loggers, property-changed handlers and the like.
It goes like this:
public void LogMessage(string message, [CallerMemberName] string caller = "")
{
// caller should contain the name of the method that called LogMessage.
}
I don't know of any limitation this has with async
methods.
I used a mix of Nathan's and Mike's answers.
Using GetType()
to query the method from stack trace did not work for me. Not guaranteed. But using CallerMemberNameAttrinute
lets me get the exact method by its name.
So my code would be:
using System.Runtime.CompilerServices;
public static void Main(string[] args)
{
Test().Wait();
}
private static async Task Test()
{
await Task.Yield();
Log();
await Task.Yield();
}
private static void Log([CallerMemberName]string methodName = "")
{
var method = new StackTrace()
.GetFrames()
.Select(frame => frame.GetMethod())
.FirstOrDefault(item => item.Name == methodName);
Console.WriteLine("Log: {0}", method.DeclaringType + "." + method.Name);
}
This way I get method name with its full namespace path.
You need to capture the method name early in the async method, somewhere before the first async call. The most convenient way I've found to skip past the compiler generated state machine is to look at the declaring type of each method in the stack trace.
var method = new StackTrace()
.GetFrames()
.Select(frame => frame.GetMethod())
.FirstOrDefault(item => item.DeclaringType == GetType());
await Task.Yield();
if (method != null)
{
Console.WriteLine(method.Name);
}