[英]Using reflection to get method name inside an async method does not return expected result
以下是我编写的一小段代码,以演示此问题的基础。
private async void Form1_Load( object sender, EventArgs e ) {
var result = await TestAsyncMethodName();
}
private async Task<string> TestAsyncMethodName() {
string name = "Method: " + System.Reflection.MethodBase.GetCurrentMethod().Name;
int x = 0;
foreach ( StackFrame sf in (new StackTrace()).GetFrames()) {
x++;
name = name + "\nStack Frame [" + x + "] : " + sf.GetMethod().Name;
}
await Task.Run( () => { name = name + "\nAnonymous Method: " + System.Reflection.MethodBase.GetCurrentMethod().Name; } );
return name;
}
Method: MoveNext
Stack Frame [1] : MoveNext
Stack Frame [2] : Start
Stack Frame [3] : TestAsyncMethodName
Stack Frame [4] : MoveNext
Stack Frame [5] : Start
Stack Frame [6] : Form1_Load
Stack Frame [7] : OnLoad
Stack Frame [8] : OnCreateControl
Stack Frame [9] : CreateControl
Stack Frame [10] : CreateControl
Stack Frame [11] : WmShowWindow
Stack Frame [12] : WndProc
Stack Frame [13] : WndProc
Stack Frame [14] : WmShowWindow
Stack Frame [15] : WndProc
Stack Frame [16] : OnMessage
Stack Frame [17] : WndProc
Stack Frame [18] : DebuggableCallback
Stack Frame [19] : ShowWindow
Stack Frame [20] : SetVisibleCore
Stack Frame [21] : SetVisibleCore
Stack Frame [22] : set_Visible
Stack Frame [23] : RunMessageLoopInner
Stack Frame [24] : RunMessageLoop
Stack Frame [25] : Run
Stack Frame [26] : Main
Stack Frame [27] : _nExecuteAssembly
Stack Frame [28] : ExecuteAssembly
Stack Frame [29] : RunUsersAssembly
Stack Frame [30] : ThreadStart_Context
Stack Frame [31] : RunInternal
Stack Frame [32] : Run
Stack Frame [33] : Run
Stack Frame [34] : ThreadStart
Anonymous Method: <TestAsyncMethodName>b__0
Method: TestAsyncMethodName
Stack Frame [1] : MoveNext
Stack Frame [2] : Start
Stack Frame [3] : TestAsyncMethodName
Stack Frame [4] : MoveNext
Stack Frame [5] : Start
Stack Frame [6] : Form1_Load
Stack Frame [7] : OnLoad
Stack Frame [8] : OnCreateControl
Stack Frame [9] : CreateControl
Stack Frame [10] : CreateControl
Stack Frame [11] : WmShowWindow
Stack Frame [12] : WndProc
Stack Frame [13] : WndProc
Stack Frame [14] : WmShowWindow
Stack Frame [15] : WndProc
Stack Frame [16] : OnMessage
Stack Frame [17] : WndProc
Stack Frame [18] : DebuggableCallback
Stack Frame [19] : ShowWindow
Stack Frame [20] : SetVisibleCore
Stack Frame [21] : SetVisibleCore
Stack Frame [22] : set_Visible
Stack Frame [23] : RunMessageLoopInner
Stack Frame [24] : RunMessageLoop
Stack Frame [25] : Run
Stack Frame [26] : Main
Stack Frame [27] : _nExecuteAssembly
Stack Frame [28] : ExecuteAssembly
Stack Frame [29] : RunUsersAssembly
Stack Frame [30] : ThreadStart_Context
Stack Frame [31] : RunInternal
Stack Frame [32] : Run
Stack Frame [33] : Run
Stack Frame [34] : ThreadStart
Anonymous Method: <TestAsyncMethodName>b__0
堆栈溢出
微软
更改
string name = "Method: " + System.Reflection.MethodBase.GetCurrentMethod().Name;
至
string name = "Method: " + GetActualAsyncMethodName();
然后将该方法实现为:
static string GetActualAsyncMethodName([CallerMemberName]string name = null) => name;
使用Async
您的代码将转换为状态机,因此堆栈跟踪和运行时信息将反映生成的代码。 如果打开您的程序集,其中代码位于ILSPLY中。 在生成状态机后,您的方法将如下所示:
.class nested private auto ansi sealed beforefieldinit '<TestAsyncMethodName>d__2'
extends [mscorlib]System.Object
implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public int32 '<>1__state'
.field public valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<string> '<>t__builder'
.field public class WindowsFormsApp_Recipients.TestForm '<>4__this'
.field private class WindowsFormsApp_Recipients.TestForm/'<>c__DisplayClass2_0' '<>8__1'
.field private int32 '<x>5__2'
.field private class [mscorlib]System.Diagnostics.StackFrame[] '<>s__3'
.field private int32 '<>s__4'
.field private class [mscorlib]System.Diagnostics.StackFrame '<sf>5__5'
.field private valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter '<>u__1'
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x279e
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method '<TestAsyncMethodName>d__2'::.ctor
.method private final hidebysig newslot virtual
instance void MoveNext () cil managed
{
.override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext()
// Method begins at RVA 0x28b0
// Code size 445 (0x1bd)
.maxstack 5
.locals init (
.................
.................
More IL CODE HERE...
如您所见,您有一个新的类类型<TestAsyncMethodName>d__2
,该类将方法的实际逻辑包装在生成的方法MoveNext
。 因此,当您要求System.Reflection.MethodBase.GetCurrentMethod().Name
,它将为您提供MoveNext
而不是您的实际方法名称。
为了获得正确的结果,您可以在此处进行修改:
var regex = new Regex(@"<(\w+)>.*");
string name = "Method: " + regex.Match(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name).Groups[1].Value;
由于状态机代码生成会为方法名称生成一种类型,该类型被标记为异步,因此此Hack可以正常工作。
目前使用的最便宜,最简单的解决方法是声明一个字符串变量,该变量充当名称的容器,然后根据需要调用此变量。 例子如下:
public async Task<string> Foo() {
string __FUNCTION__ = "Foo";
// await / etc code here
}
另一种比字符串便宜(内存)的方法是为您的方法创建一个枚举并将其转换为字符串。 设置方法名称时,这在内存中会便宜一些,但是在转换回字符串以供使用时,它会稍微贵一些。 下面是一个示例:
public class MyClass {
public enum __FUNCTIONS {
Foo,
Bar
}
public async Task<string> Foo() {
__FUNCTIONS __FUNCTION__ = __FUNCTIONS.Foo;
// await / etc code here
}
public async Task<string> Bar() {
__FUNCTIONS __FUNCTION__ = __FUNCTIONS.Bar;
// await / etc code here
}
}
不幸的是,到目前为止,Visual Studio中还没有真正的魔术常数支持,而且只要该语言是JIT IL,它似乎就不会在将来发生,因为该技术似乎仅限于通过反射来提供,而不是通过看起来本身意识到的代码来提供(例如PHP的__FUNCTION__
等)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.