2013-11-23 40 views
15

反正有没有从异步函数中获取当前方法名?从异步函数中获取当前方法名称?

我已经试过:

System.Reflection.MethodInfo.GetCurrentMethod(); 

而且我已经使用堆栈跟踪和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“

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

我放弃了这一点,并说,好吧,我会传入函数名称作为第一个参数,但是当我从调用通用记录器函数的调用函数调用反射方法时,我总是得到“。 ctor“

任何想法?请注意,我调用的通用记录器函数是同一类中的静态方法(现在它必须是这种方式)。

+0

P.S. [EventSource> P/Invoke](http://blogs.msdn.com/b/vancem/archive/tags/eventsource/) –

回答

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

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属性,可以让其他片约通话网站信息的。

+0

不幸的是,我现在无法针对C#5.0进行编译(虽然我会仔细检查一下)。 有没有其他解决方案想到? – user2939634

+0

@ user2939634那你怎么编译'async'?您是否使用.Net 4.0的Async定位包?如果是这样,你可以在'System.Runtime.CompilerServices'命名空间中定义自己的属性'CallerMemberName',它应该可以工作(正如在评论中提到的另一个答案中的svick)。 –

+1

通过使用调用者信息属性来获取方法名称在全局错误记录器中没有用处,因为您无法获得引发异常的调用堆栈中的第一个方法。 [CallerMemberName]只获取调用全局错误处理函数的方法的名称。 –

6

而不是手动堆栈帧走,这既昂贵又风险(因为发布版本可能会优化某些方法),您可以使用CallerMemberNameAttribute,其中一个Caller 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方法的任何限制。

+0

调用者成员属性并不是.Net 4.5的一个特性,它们是C#5.0的一个特性。如果您使用C#5.0来定位.Net 4.0,那么您可以自己定义该属性,它将起作用。 – svick

+0

@svick你可以发表一个例子或给我们一个链接? –

+1

@StefanVasiljevic [此代码](https://gist.github.com/svick/97c6a8c8b006283c96e2)适合我。 – svick

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

我希望这有助于!

0

可以发送strackTrace.GetFrame(ⅰ).GetMethod(),它是一个对象System.Reflection.MethodBase该检查,如果System.Reflection.MethodBase.DeclaringType.FullName具有<>字符,并且如果功能所以会得到请求的方法名称,它位于< ...> chars之间。

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

这种方法从异步方法调用以及从正常方法效果。 (C#5)

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