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

如何构建依赖宿主传入配置项的Roslyn源代码生成器流水线?

Roslyn增量生成器:动态特性名称的高效处理方案

你当前尝试在Select委托中嵌套创建SyntaxProvider的方式不符合Roslyn增量生成器的设计规范,会破坏缓存机制,且无法正确展平嵌套的IncrementalValuesProvider。以下是两种可行的解决方案:


方案一:静态选项场景(性能最优)

如果特性名称是编译前就确定的静态配置(比如从项目文件的AnalyzerConfig选项读取),可以直接在初始化阶段创建多个ForAttributeWithMetadataName提供器并合并,完全保留其高性能优势:

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    // 1. 读取全局配置中的特性名称列表
    var globalOptions = context.AnalyzerConfigOptionsProvider.GlobalOptions;
    globalOptions.TryGetValue("your_config_key", out var attributeNamesStr);
    var attributeNames = attributeNamesStr?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();

    // 2. 为每个特性名称创建独立的高效提供器
    var typeProviders = new List<IncrementalValuesProvider<TypeDeclarationSyntax>>();
    foreach (var attrName in attributeNames)
    {
        var provider = context.SyntaxProvider.ForAttributeWithMetadataName(
            attributeName: attrName,
            predicate: (node, _) => node is TypeDeclarationSyntax,
            transform: (ctx, _) => (TypeDeclarationSyntax)ctx.Node
        );
        typeProviders.Add(provider);
    }

    // 3. 合并所有提供器的输出
    IncrementalValuesProvider<TypeDeclarationSyntax> mergedProvider;
    if (typeProviders.Count == 0)
    {
        // 空提供器,避免后续报错
        mergedProvider = context.SyntaxProvider.CreateSyntaxProvider((_,__) => false, (_,__) => null)
            .Where(_ => false);
    }
    else
    {
        mergedProvider = typeProviders[0];
        for (int i = 1; i < typeProviders.Count; i++)
        {
            mergedProvider = mergedProvider.Concat(typeProviders[i]);
        }
    }

    // 4. 后续转换与代码生成(按你的需求处理TypeDeclarationSyntax)
    var finalDataProvider = mergedProvider.Select((typeDecl, ct) => 
    {
        // 提取生成代码所需的数据,确保缓存生效
        return YourTransformationLogic(typeDecl, ct);
    });

    context.RegisterSourceOutput(finalDataProvider, (spc, data) => 
    {
        // 生成代码
        YourCodeGenerationLogic(spc, data);
    });
}

方案二:动态选项场景(兼容动态配置)

如果特性名称可能在编译过程中动态变化,只能使用CreateSyntaxProvider结合选项列表过滤,牺牲部分性能但支持动态配置:

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    // 1. 将配置转换为可缓存的特性名称集合
    var targetAttributesProvider = context.AnalyzerConfigOptionsProvider
        .Select((optionsProvider, _) =>
        {
            optionsProvider.GlobalOptions.TryGetValue("your_config_key", out var attributeNamesStr);
            var attributeNames = attributeNamesStr?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
            return new HashSet<string>(attributeNames);
        })
        .WithComparer(HashSet<string>.CreateSetComparer()); // 设置比较器确保缓存生效

    // 2. 创建语法提供器,筛选带有目标特性的类型
    var typesProvider = context.SyntaxProvider.CreateSyntaxProvider(
        predicate: (node, _) => node is TypeDeclarationSyntax typeDecl && typeDecl.AttributeLists.Any(),
        transform: (ctx, ct) =>
        {
            var typeDecl = (TypeDeclarationSyntax)ctx.Node;
            var semanticModel = ctx.SemanticModel;
            var targetAttributes = targetAttributesProvider.GetValue(ct);

            // 检查类型是否带有目标特性之一
            var hasMatchingAttribute = typeDecl.AttributeLists
                .SelectMany(list => list.Attributes)
                .Select(attr => semanticModel.GetSymbolInfo(attr).Symbol as IMethodSymbol)
                .Where(ctor => ctor != null)
                .Any(ctor => targetAttributes.Contains(ctor.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));

            return hasMatchingAttribute ? typeDecl : null;
        })
        .Where(typeDecl => typeDecl != null); // 过滤掉不匹配的类型

    // 3. 后续转换与代码生成
    var finalDataProvider = typesProvider.Select((typeDecl, ct) => 
    {
        return YourTransformationLogic(typeDecl, ct);
    });

    context.RegisterSourceOutput(finalDataProvider, (spc, data) => 
    {
        YourCodeGenerationLogic(spc, data);
    });
}

关键注意事项

  • 绝对不要在IncrementalValuesProvider的委托(如Select)中创建新的SyntaxProvider,这会破坏Roslyn的缓存生命周期,导致性能下降和缓存失效。
  • 静态方案的性能远高于动态方案,优先使用静态配置场景的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:50:58