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

如何在C#源生成器中获取泛型方法调用的类型参数?

在C#源生成器中获取泛型方法调用的类型参数(适配Bevy风格ECS)

一、精准定位目标方法的调用站点

用ISyntaxReceiver定向收集目标调用,避免无差别扫描所有语法节点:

  • 重写OnVisitSyntaxNode,筛选所有InvocationExpressionSyntax
  • 结合SemanticModel验证调用的方法是否为App.RunSystem<T>(Action<T>):
    1. 获取调用的方法符号IMethodSymbol
    2. 校验方法名、所属类型(App类)、命名空间,确认是泛型方法且泛型参数数量为1
    3. 验证参数列表匹配(第一个参数为Action<T>类型)

代码示例:

public class RunSystemInvocationReceiver : ISyntaxReceiver
{
    public List<InvocationExpressionSyntax> Invocations { get; } = new();

    public void OnVisitSyntaxNode(SyntaxNode node)
    {
        if (node is InvocationExpressionSyntax invocation
            && invocation.Expression is MemberAccessExpressionSyntax memberAccess
            && memberAccess.Name.Identifier.Text == "RunSystem")
        {
            Invocations.Add(invocation);
        }
    }
}

// 在生成器Execute方法中筛选有效调用
var semanticModel = context.Compilation.GetSemanticModel(invocation.SyntaxTree);
var methodSymbol = semanticModel.GetSymbolInfo(invocation.Expression).Symbol as IMethodSymbol;
if (methodSymbol != null
    && methodSymbol.Name == "RunSystem"
    && methodSymbol.ContainingType.Name == "App"
    && methodSymbol.ContainingNamespace.ToDisplayString() == "Your.Namespace"
    && methodSymbol.IsGenericMethod
    && methodSymbol.TypeParameters.Length == 1
    && methodSymbol.Parameters[0].Type is INamedTypeSymbol actionType
    && actionType.Name == "Action"
    && actionType.TypeArguments.Length == 1)
{
    // 确认是目标RunSystem<T>调用
}

二、获取泛型类型参数(显式+推断)

不管是显式指定还是类型推断,编译器已完成类型解析,直接从IMethodSymbol获取即可:

1. 显式指定的情况(如app.RunSystem<SomeType>(...))

无需手动解析语法节点的类型参数,直接用语义分析结果:

// 从方法符号获取已解析的类型参数
var systemType = methodSymbol.TypeArguments[0];
// 获取类型的完全限定名
var typeFullName = systemType.ToDisplayString();

2. 类型推断的情况(如app.RunSystem((SomeType t) => { }))

编译器完成类型推断后,methodSymbol.TypeArguments会自动包含推断出的类型,无需手动解析lambda参数:

// 直接取已推断的类型参数
var inferredType = methodSymbol.TypeArguments[0];
// 可选:验证推断类型与lambda参数类型一致
var lambdaArgSymbol = semanticModel.GetSymbolInfo(invocation.ArgumentList.Arguments[0].Expression).Symbol as IParameterSymbol;
if (lambdaArgSymbol != null && lambdaArgSymbol.Type.Equals(inferredType, SymbolEqualityComparer.Default))
{
    // 类型推断结果符合预期
}

三、Bevy风格ECS的源生成器优化建议

针对AddSystem和Query场景,生成强类型代码彻底规避反射与DynamicInvoke,适配AOT平台:

  • AddSystem场景:为每个System生成静态强类型扩展方法,固化泛型参数,比如生成AppExtensions.AddSomeTypeSystem(this App app, Action<SomeType> action),调用时直接使用强类型方法
  • Query场景:为每种组件组合生成强类型查询器,比如Query<ComponentA, ComponentB>对应生成Query_ComponentA_ComponentB类,内置强类型实体迭代逻辑,避免运行时类型检查
  • AOT适配:所有生成代码均为静态编译,无动态表达式或反射调用,完全符合AOT平台要求

生成代码示例:

// 自动生成的强类型扩展方法
public static class AppSystemExtensions
{
    public static void AddSomeTypeSystem(this App app, Action<SomeType> action)
    {
        app.RegisterSystem(typeof(SomeType), action);
    }
}

// 自动生成的强类型Query
public class Query_SomeType : IQuery
{
    public void Iterate(World world, Action<SomeType> callback)
    {
        foreach (var entity in world.EntitiesWith<SomeType>())
        {
            callback(entity.GetComponent<SomeType>());
        }
    }
}

内容的提问来源于stack exchange,提问作者Clonkex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 21:12:52