从异步函数中获取当前方法名称?

时间:2013-11-23 05:22:29

标签: c# async-await

无论如何从异步函数中获取当前方法名称?

我试过了:

System.Reflection.MethodInfo.GetCurrentMethod();

我尝试使用StackTrace和StrackFrame如下:

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

但它们似乎都不起作用,我会得到“.ctor”,“InvokeMethod”,“Invoke”,“CreateInstance”,“CreateKnownObject”或“CreateUnknownObject”或“MoveNext”

关于我如何做到这一点的任何想法?我想创建一个通用的记录器函数,我不想传入调用记录器函数的函数的名称,所以我尝试了栈跟踪方法,但是没有用。

我放弃了并且说,好吧,我将传递函数名作为第一个参数,但是当我从调用泛型记录器函数的调用函数调用反射方法时,我总是得到“.ctor “

有什么想法吗?注意我正在调用的通用记录器函数是同一个类中的静态方法(现在必须以这种方式...)。

9 个答案:

答案 0 :(得分:16)

C#5添加了来电者信息属性,可以为您提供更多您想要的内容。请注意,这些信息会在编译时将适当的信息插入到调用站点中,而不是使用运行时信息。功能更加有限(显然你无法获得完整的调用堆栈),但速度要快得多。

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

还有CallerFilePath和CallerLineNumber属性,可以获取有关呼叫站点的其他信息。

答案 1 :(得分:8)

您可以使用CallerMemberNameAttributeCaller Information属性之一,而不是进行手动堆栈帧漫步,这既昂贵又有风险(因为版本构建可能会优化某些方法)在.NET 4.5(已经使用,如果你使用async / await)中添加了这个确切的场景 - 传入记录器,属性更改处理程序等的成员名称。

它是这样的:

public void LogMessage(string message, [CallerMemberName] string caller = "")
{ 
    // caller should contain the name of the method that called LogMessage.
}

我不知道这对async方法有任何限制。

答案 2 :(得分:6)

此方法适用于 async 方法调用以及普通方法。 (C#5)

/// <summary>
///     Returns Current method name
/// </summary>
/// <returns>callers method name</returns>
public string GetCurrentMethod([CallerMemberName] string callerName = "")
{
    return callerName;
}

答案 3 :(得分:1)

我最近在日志消息中遇到了相同的问题,这里的答案很有帮助。我正在分享我的代码,以防它可以帮助其他人解决问题。

using System.Runtime.CompilerServices;

namespace Foo.Services
{
    public interface ILogFormatter
    {
        public string FormatMessage(LogType logType, string details, [CallerMemberName] string caller = "");
    }


    public enum LogType
    {
        Debug,
        Information,
        Warning,
        Error
    }

    public class LogFormatterOptions
    {
        public string Subject { get; set; }
    }

    public class LogFormatter : ILogFormatter
    {
        public LogFormatter(LogFormatterOptions options)
        {
            Subject = options.Subject;
        }

        public string Subject { get; set; }

        public string FormatMessage(LogType logType, string details, [CallerMemberName] string caller = "")
        {
            return $"{Subject} - {logType}: caller: {caller} - {details}";
        }
    }
}

这是调用此方法的方式,它适用于异步方法和同步方法。请注意,我没有传递调用者输入的值,而是获得了调用者方法的名称。

 logger.LogInformation(logFormatter.FormatMessage(LogType.Information, ""));

答案 4 :(得分:1)

  1. 使用 System.Runtime.CompilerServices;

  2. 在静态类文件 (ExtensionHelper) 中创建静态方法 (GetActualAsyncMethodName)。

公共静态类 ExtensionHelper { public static string GetActualAsyncMethodName([CallerMemberName] string name = null) => name; }

  1. 调用 GetActualAsyncMethodName() 以获取异步方法名称。

    string methodName = ExtensionHelper.GetActualAsyncMethodName();

答案 5 :(得分:0)

您需要在异步方法的早期捕获方法名称,在第一次异步调用之前的某个位置。我发现跳过编译器生成的状态机的最方便的方法是查看堆栈跟踪中每个方法的声明类型。

var method = new StackTrace()
    .GetFrames()
    .Select(frame => frame.GetMethod())
    .FirstOrDefault(item => item.DeclaringType == GetType());
await Task.Yield();
if (method != null)
{
    Console.WriteLine(method.Name);
}

答案 6 :(得分:0)

我想回答你的问题有点晚了但是因为我遇到了同样的问题而我需要自己解决这个问题,因为在互联网上缺乏一个好的解决方案,这就是我所做的和它至少对我来说是完美的 -

  1. 在所有调用者都可以访问的地方定义以下Regex,因为Regex对象在使用之前需要进行解析;因此,它可能很昂贵,如果我们可以使用同一个,那么反复创建它们并不是一个好习惯。
    public static readonly Regex ASYNC_METHOD_NAME_FORMAT = new Regex(@"^\<(?\w+)>\w+?");
  2. 使用以下代码获取方法名称
    string methodName = ASYNC_METHOD_NAME_FORMAT.Match(MethodBase.GetCurrentMethod().ReflectedType.Name).Groups["method_name"].Value
  3. 我希望这有帮助!

答案 7 :(得分:0)

您可以将一个System.Reflection.MethodBase对象发送到一个函数strackTrace.GetFrame(i).GetMethod(),该函数检查System.Reflection.MethodBase.DeclaringType.FullName是否具有&lt;&gt; chars,如果是这样,将获得在&lt; ...&gt;之间放置的请求方法名称字符。

static private string getMethodName(System.Reflection.MethodBase method)
    {
        string _methodName = method.DeclaringType.FullName;

        if (_methodName.Contains(">") || _methodName.Contains("<"))
        {
            _methodName = _methodName.Split('<', '>')[1];
        }
        else
        {
            _methodName = method.Name;
        }

        return _methodName;
    }

如何使用示例:

var _stackTrace = new System.Diagnostics.StackTrace(exception, true);
string _methodName = getMethodName(_stackTrace.GetFrame(0).GetMethod());

答案 8 :(得分:0)

我混合使用了内森(Nathan)和迈克(Mike)的答案。

使用GetType()从堆栈跟踪中查询方法对我不起作用。不保证。但是使用CallerMemberNameAttrinute可以让我按名称获得确切的方法。

所以我的代码是:

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

这样,我便获得了具有完整名称空间路径的方法名称。