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

在C#增量源生成器(IIncrementalGenerator)中获取基类属性

问题描述

我在源生成器中编写了如下Execute方法,能够获取当前类的所有属性(通过x.Item1.Members.AsEnumerable()...),但无法获取基类的属性。请问如何列出基类的所有属性?

private static void Execute(Compilation compilation, ImmutableArray<(ClassDeclarationSyntax, AttributeData)> classes, SourceProductionContext context)
{
    foreach (var (x, i) in classes.Select((x, i) => (x, i)))
    {
        TypedConstant aggregateParam = x.Item2.ConstructorArguments[0];
        if (aggregateParam.Kind == TypedConstantKind.Primitive &&
            aggregateParam.Value is string fullyQualifiedAggregateName)
        {
            var aggregateName = "Aggregate";

            var props = x.Item1.Members.AsEnumerable().Where(o => o.IsKind(SyntaxKind.PropertyDeclaration));

            var sb = new StringBuilder();

            context.AddSource(
                $"generated_{aggregateName}_{i}.g.cs",
                sb.ToString());
        }
    }
}

更新

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    IncrementalValuesProvider<(ClassDeclarationSyntax, AttributeData)> classDeclarations = context.SyntaxProvider
        .ForAttributeWithMetadataName(
            EventApplyAttribute,
            predicate: static (node, _) => node is ClassDeclarationSyntax,
            transform: static (ctx, ct) => GetSemanticTargetForGeneration(ctx, ct))
        .Where(m => m.Item1 is not null && m.Item2 is not null);

    IncrementalValueProvider<(Compilation, ImmutableArray<(ClassDeclarationSyntax, AttributeData)>)> compilationAndClasses
        = context.CompilationProvider.Combine(classDeclarations.Collect());

    context.RegisterSourceOutput(compilationAndClasses,
        static (spc, source) => Execute(source.Item1, source.Item2, spc));
}

private static (ClassDeclarationSyntax, AttributeData) GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct)
{
    if (context.TargetNode is not ClassDeclarationSyntax classDeclaration)
    {
        return (null, null);
    }

    AttributeData? attribute = context.Attributes.FirstOrDefault(a => a.AttributeClass?.Name == "EventApplyAttribute");

    return (classDeclaration, attribute);
}
解决方案

仅通过语法节点ClassDeclarationSyntax只能获取当前类定义的成员,要拿到基类属性,必须借助语义分析,通过类的符号INamedTypeSymbol遍历整个继承链。

具体实现步骤

  1. 获取类的语义符号
    在Execute方法中,利用编译实例和语法节点获取对应的类符号:
var semanticModel = compilation.GetSemanticModel(x.Item1.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(x.Item1, context.CancellationToken) is not INamedTypeSymbol classSymbol)
{
    continue;
}
  1. 编写辅助方法遍历继承链
    递归或循环遍历类的基类,收集所有非编译器自动生成的属性:
static IEnumerable<IPropertySymbol> GetAllProperties(INamedTypeSymbol typeSymbol, CancellationToken ct)
{
    var currentType = typeSymbol;
    var properties = new List<IPropertySymbol>();

    while (currentType != null)
    {
        // 筛选当前类的属性,排除编译器合成的隐式成员
        properties.AddRange(currentType.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => !p.IsImplicitlyDeclared));
        
        // 移动到下一层基类
        currentType = currentType.BaseType;
        ct.ThrowIfCancellationRequested();
    }

    return properties;
}
  1. 替换原有属性获取逻辑
    在Execute中调用辅助方法,替换原来仅获取当前类属性的代码:
// 替换原props获取逻辑
var allProperties = GetAllProperties(classSymbol, context.CancellationToken);
// 可按需筛选,比如只保留公共属性:
// var publicProps = allProperties.Where(p => p.DeclaredAccessibility == Accessibility.Public);

修改后的完整Execute方法示例

private static void Execute(Compilation compilation, ImmutableArray<(ClassDeclarationSyntax, AttributeData)> classes, SourceProductionContext context)
{
    foreach (var (x, i) in classes.Select((x, i) => (x, i)))
    {
        TypedConstant aggregateParam = x.Item2.ConstructorArguments[0];
        if (aggregateParam.Kind == TypedConstantKind.Primitive &&
            aggregateParam.Value is string fullyQualifiedAggregateName)
        {
            var aggregateName = "Aggregate";

            // 获取语义模型与类符号
            var semanticModel = compilation.GetSemanticModel(x.Item1.SyntaxTree);
            if (semanticModel.GetDeclaredSymbol(x.Item1, context.CancellationToken) is not INamedTypeSymbol classSymbol)
            {
                continue;
            }

            // 获取当前类及所有基类的属性
            var allProperties = GetAllProperties(classSymbol, context.CancellationToken);

            var sb = new StringBuilder();
            // 此处可使用allProperties生成目标代码

            context.AddSource(
                $"generated_{aggregateName}_{i}.g.cs",
                sb.ToString());
        }
    }
}

static IEnumerable<IPropertySymbol> GetAllProperties(INamedTypeSymbol typeSymbol, CancellationToken ct)
{
    var currentType = typeSymbol;
    var properties = new List<IPropertySymbol>();

    while (currentType != null)
    {
        properties.AddRange(currentType.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => !p.IsImplicitlyDeclared));
        
        currentType = currentType.BaseType;
        ct.ThrowIfCancellationRequested();
    }

    return properties;
}

注意事项

  • 必须处理取消令牌CancellationToken,避免生成器运行阻塞
  • 可根据需求添加筛选条件,比如仅保留公共属性、排除抽象属性等
  • 继承链终点为object类,此时currentType.BaseType会返回null,循环自动终止

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:35:59