Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get current method name from async function?

Tags:

c#

async-await

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

like image 292
user2939634 Avatar asked Nov 23 '13 05:11

user2939634


4 Answers

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.

like image 84
Mike Zboray Avatar answered Oct 12 '22 14:10

Mike Zboray


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;
}
like image 41
Vojta Avatar answered Oct 12 '22 12:10

Vojta


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.

like image 11
Avner Shahar-Kashtan Avatar answered Oct 12 '22 13:10

Avner Shahar-Kashtan


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);
}
like image 2
Nathan Baulch Avatar answered Oct 12 '22 12:10

Nathan Baulch