You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中调用方为异步方法时如何获取其原始方法名称

解决方案

核心原因

C# 编译器会为 async/await 方法自动生成实现了 IAsyncStateMachine 接口的嵌套状态机类,原方法的业务逻辑会被拆分到该状态机类的 MoveNext 方法中执行,因此直接读取调用栈帧拿到的方法名是 MoveNext,属于编译器生成的嵌套类,而非你编写的原始业务方法。


最优方案:用CallerMemberName特性跳过栈读取

这种方案性能远高于读取调用栈,且天然兼容同步/异步方法,不需要处理状态机逻辑:

  1. 给 CanUserEditProgram 方法增加标记了 [CallerMemberName] 的可选参数,编译器会自动将调用方的原始方法名赋值给该参数,无需手动传参
  2. 直接用拿到的方法名反射读取自定义属性即可

修改后代码如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:30:00