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

能否通过C# IIncrementalGenerator向其他项目生成代码?

跨项目源代码生成问题

我正在编写一个实现IIncrementalGenerator的源代码生成器,需要读取一个所有属性均为常量的struct,示例如下:

public struct MyStruct
{
    public const string Prop1 = "PROP1";
    // More properties
}

我已经通过以下代码,在该struct所在的同一项目中成功生成代码:

[Generator]
public class MyGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var structs = context.SyntaxProvider
          .CreateSyntaxProvider(
             (s, _) => s is StructDeclarationSyntax sds,
             (c, _) =>
             {
                 return (c.Node is StructDeclarationSyntax sds && sds.Identifier.Text == "MyStruct")
                     ? sds
                     : null;
             }
          )
          .Where(x => x != null)
          .Collect();

        context.RegisterSourceOutput(structs, GenerateSource);
    }

    private void GenerateSource(SourceProductionContext context, ImmutableArray<StructDeclarationSyntax?> structs)
    {            
        string myCode = // my generated code
        context.AddSource($"MyCode.g.cs", myCode);
    }
}

现在我希望向另一个项目生成额外代码,请问通过IncrementalGeneratorInitializationContext和SourceProductionContext是否可行?

我的思路:

  • 是否应使用AdditionalTextsProvider?
  • 是否需要在另一个项目中编写新生成器,并访问原项目中的MyStruct?如果是,该如何实现?

解决方案

核心结论

直接通过当前生成器向其他项目输出代码是不可行的,因为源代码生成器的作用域被限制在其运行的项目内——SourceProductionContext.AddSource只能将代码添加到当前生成器所在的项目(或引用该生成器的项目),无法跨项目输出。


针对思路的具体解答

1. AdditionalTextsProvider是否适用?

AdditionalTextsProvider用于读取项目中的额外文件(如JSON、XML等),但它无法让生成器直接访问另一个项目中的编译符号(比如MyStruct的常量),所以这个方案不适用你的需求。

2. 跨项目生成的正确实现方式

你需要在目标项目中引用生成器,并让生成器能够读取原项目中MyStruct的元数据,具体步骤如下:

步骤1:确保原项目的MyStruct能被目标项目访问
  • 将定义MyStruct的项目设为类库,让目标项目通过项目引用或NuGet包引用它。
  • 注意:生成器只能读取编译后的元数据,所以MyStruct必须是公共可见的(public修饰符),且其常量属性也需是公共的。
步骤2:修改生成器,通过语义模型读取元数据

原生成器是通过语法树(StructDeclarationSyntax)读取MyStruct,这种方式只能访问当前项目内的语法节点。跨项目时需要改用语义分析,读取已编译的类型符号:

[Generator]
public class MyGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 注册编译提供器,获取当前项目的编译上下文
        var compilationProvider = context.CompilationProvider;

        context.RegisterSourceOutput(compilationProvider, (ctx, compilation) =>
        {
            // 查找原项目中的MyStruct类型符号(替换为实际命名空间)
            var myStructType = compilation.GetTypeByMetadataName("YourNamespace.MyStruct");
            if (myStructType == null)
                return;

            // 遍历所有公共常量属性
            var constantProperties = myStructType.GetMembers()
                .OfType<IFieldSymbol>()
                .Where(f => f.IsConst && f.IsPublic);

            // 生成目标项目所需的代码
            string generatedCode = GenerateCrossProjectCode(constantProperties);
            ctx.AddSource("CrossProjectCode.g.cs", generatedCode);
        });
    }

    private string GenerateCrossProjectCode(IEnumerable<IFieldSymbol> constants)
    {
        // 编写你的代码生成逻辑,示例为生成常量类
        var codeBuilder = new StringBuilder();
        codeBuilder.AppendLine("namespace CrossProjectGenerated;");
        codeBuilder.AppendLine("public static class MyConstants");
        codeBuilder.AppendLine("{");
        foreach (var constant in constants)
        {
            codeBuilder.AppendLine($"    public const string {constant.Name} = \"{constant.ConstantValue}\";");
        }
        codeBuilder.AppendLine("}");
        return codeBuilder.ToString();
    }
}
步骤3:在目标项目中配置生成器
  • 将生成器项目打包为NuGet包,或通过项目引用添加到目标项目。
  • 确保目标项目引用了包含MyStruct的原项目类库。
  • 当目标项目编译时,生成器会读取MyStruct的元数据,在目标项目内生成代码。

关键注意事项

  • 生成器运行在目标项目的编译阶段,只能访问目标项目引用的已编译程序集,无法直接读取原项目的源代码。
  • 如果MyStruct的常量值发生变化,需要重新编译原项目,目标项目才能获取到更新后的元数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:43:08