C#中调用方为异步方法时如何获取其原始方法名称
解决方案
核心原因
C# 编译器会为 async/await 方法自动生成实现了 IAsyncStateMachine 接口的嵌套状态机类,原方法的业务逻辑会被拆分到该状态机类的 MoveNext 方法中执行,因此直接读取调用栈帧拿到的方法名是 MoveNext,属于编译器生成的嵌套类,而非你编写的原始业务方法。
最优方案:用CallerMemberName特性跳过栈读取
这种方案性能远高于读取调用栈,且天然兼容同步/异步方法,不需要处理状态机逻辑:
- 给
CanUserEditProgram方法增加标记了[CallerMemberName]的可选参数,编译器会自动将调用方的原始方法名赋值给该参数,无需手动传参 - 直接用拿到的方法名反射读取自定义属性即可
修改后代码如下:
using System.Runtime.CompilerServices; // 先修改CanUserEditProgram方法 public bool CanUserEditProgram(int program_id, [CallerMemberName] string callerMethodName = "") { string[] allowed_roles = GetMethodRoles(callerMethodName); // 后续原有逻辑不变 } // 修改GetMethodRoles,接收外部传入的调用方方法名 public string[] GetMethodRoles(string callerMethodName) { string[] roles = { }; StackTrace stackTrace = new StackTrace(); // 从栈帧取调用方所属类 var frameMethod = stackTrace.GetFrame(2).GetMethod(); var callerType = frameMethod.ReflectedType; // 兼容异步的情况,如果是状态机类,取其外层声明类 if (callerType.GetCustomAttribute<CompilerGeneratedAttribute>() != null && typeof(IAsyncStateMachine).IsAssignableFrom(callerType)) { callerType = callerType.DeclaringType; } // 直接用传入的原始方法名查找对应方法 MethodInfo mInfo = callerType.GetMethod(callerMethodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); if (mInfo == null) return roles; bool isDef = Attribute.IsDefined(mInfo, typeof(MELAdvanceRoles)); if (isDef) { MELAdvanceRoles obsAttr = (MELAdvanceRoles)Attribute.GetCustomAttribute(mInfo, typeof(MELAdvanceRoles)); if (obsAttr != null) { roles = obsAttr._Roles; } } return roles; }
如果你坚持要完全通过调用栈获取原始方法名
可以通过识别状态机类,反向查找关联的原始方法,修改后的GetMethodRoles逻辑如下:
public string[] GetMethodRoles() { string[] roles = { }; StackTrace stackTrace = new StackTrace(); var frameMethod = stackTrace.GetFrame(2).GetMethod(); var methodDeclaringType = frameMethod.DeclaringType; MethodInfo originalMethod = null; // 判断是不是编译器生成的异步状态机方法 if (methodDeclaringType.GetCustomAttribute<CompilerGeneratedAttribute>() != null && typeof(IAsyncStateMachine).IsAssignableFrom(methodDeclaringType)) { // 状态机类的外层声明类就是原始方法所在的类 var originalType = methodDeclaringType.DeclaringType; // 遍历原始类的所有方法,找到关联当前状态机类的方法 originalMethod = originalType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static) .FirstOrDefault(m => m.GetCustomAttribute<AsyncStateMachineAttribute>()?.StateMachineType == methodDeclaringType); } else { // 同步方法直接用原有逻辑获取方法 originalMethod = frameMethod as MethodInfo; } if (originalMethod == null) return roles; bool isDef = Attribute.IsDefined(originalMethod, typeof(MELAdvanceRoles)); if (isDef) { MELAdvanceRoles obsAttr = (MELAdvanceRoles)Attribute.GetCustomAttribute(originalMethod, typeof(MELAdvanceRoles)); if (obsAttr != null) { roles = obsAttr._Roles; } } return roles; }
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

