获取 C# 源代码生成中的基类属性 - IIncrementalGenerator

Get base class properties in C# source generation - IIncrementalGenerator

提问人:Dani 提问时间:10/9/2023 最后编辑:Dani 更新时间:10/10/2023 访问量:118

问:

我在源代码生成器中有以下 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);
        }
C# c夏普源生成器

评论

0赞 Youssef13 10/9/2023
不得在增量管道中包含编译。
0赞 Marc Gravell 10/9/2023
@Youssef13当你在增量管道中使用时,它实际上会给你回调,这大概就是 Dani 在这里所说的,因为这也是为你提供 API 的 (在这种情况下);从字面上看,不可能避免RegisterImplementationSourceOutputCompilationImmutableArray<TSource>TSourceClassDeclarationSyntaxCompilation
0赞 Youssef13 10/9/2023
在某些情况下,很难/不可能避免这种情况,但这些情况被认为不适合源生成器。但一般来说,编译永远不应该包含在管道中。你在转换中得到它,但你不应该把它返回回来,相反,所需的信息应该被提取到一个值相等的模型中。

答:

0赞 Marc Gravell 10/9/2023 #1

真的,你想要的是语义模型,而不是语法模型。而且你不需要等待 - 当使用增量生成器时,大概你正在使用 ,在语法模型上提供预过滤器,以及更灵活的转换。在该转换中,可以访问语义模型。这对性能是有利的,因为它允许缓存转换步骤中的任何处理,只要相关语法树没有失效。ExecuteCreateSyntaxProvider

在转换过程中,您可以通过以下方式获取语义模型 - 您可能希望在 .由于这是声明一个类型,因此它给你的符号应该是 an 并且很可能是 .这意味着您现在拥有对类型数据的完全访问权限,包括 .ctx.SemanticModelctx.SemanticModel.GetDeclaredSymbol(...)ClassDeclarationSyntaxITypeSymbolINamedTypeSymbol.BaseType

但是:在转换或执行时,您可以获得语义模型

您还可以通过传入相关语法节点的语法树来获取语义模型,但是:如果您这样做,您可能会重复大量工作,从而对 IDE 性能产生不利影响。Executestate.Compilation.GetSemanticModel(...)

评论

0赞 Youssef13 10/9/2023
除此之外,该步骤应生成一个值相等的自定义模型,该模型不得包含编译或符号。相反,它应该从编译/符号中提取它需要的东西。transform
0赞 Marc Gravell 10/9/2023
@Youssef13出于好奇,有什么引用吗?
1赞 Youssef13 10/9/2023
好吧,通过 Discord 频道与 Roslyn 团队聊天时,这些信息非常隐藏。但是在 github.com/dotnet/roslyn/blob/main/docs/features/ 有一个暗示......。顺便说一句,这就是为什么很难编写增量生成器或将现有的非增量生成器转换为增量生成器的原因。还可以查看生成器的示例 GH 问题,该生成器在管道 github.com/k94ll13nn3/AutoConstructor/issues/82 中包含编译。