在C#源代码生成器中创建非C#文件的高效方案
生成非C#文件的最优高效方案
1. 通过MSBuild属性传递项目路径(推荐)
这是最可靠且优雅的方式,直接从MSBuild获取项目级路径,避免依赖语法树文件路径的不确定性:
步骤:
- 在用户项目文件(.csproj)中配置输出目录属性:
<PropertyGroup> <!-- 自定义TS文件输出目录,可根据需求修改 --> <TsOutputDir>$(ProjectDir)GeneratedTsFiles</TsOutputDir> </PropertyGroup> - 在生成器中读取该属性:
可以在Initialize方法中缓存路径,避免重复获取:[Generator] public class TsGenerator : ISourceGenerator { private string? _tsOutputDir; public void Initialize(GeneratorInitializationContext context) { // 从MSBuild全局配置中读取自定义属性 if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue( "build_property.TsOutputDir", out var outputDir)) { _tsOutputDir = outputDir; // 确保目录存在,CreateDirectory存在时不会报错 Directory.CreateDirectory(_tsOutputDir); } } public void Execute(GeneratorExecutionContext context) { if (string.IsNullOrEmpty(_tsOutputDir)) { // 处理属性未配置的 fallback 逻辑 var baseFilePath = context.Compilation.SyntaxTrees .FirstOrDefault(x => x.HasCompilationUnitRoot)?.FilePath; if (string.IsNullOrEmpty(baseFilePath)) return; _tsOutputDir = Path.Combine(Path.GetDirectoryName(baseFilePath)!, "tsFiles"); Directory.CreateDirectory(_tsOutputDir); } // 生成TS内容并写入文件 var tsContent = "export class MyTsClass {}"; var tsFilePath = Path.Combine(_tsOutputDir, "MyClass.ts"); File.WriteAllText(tsFilePath, tsContent); } }
优势:
- 路径直接来自项目配置,准确可靠,不受语法树文件位置影响
- 可在
Initialize阶段缓存,无需每次Execute重复计算 - 用户可灵活自定义输出目录,增强工具的扩展性
2. 改进路径缓存逻辑(兼容现有实现)
如果暂时不想依赖MSBuild属性,可通过缓存路径优化现有逻辑,避免重复执行路径计算和目录判断:
[Generator] public class TsGenerator : ISourceGenerator { private string? _cachedOutputDir; public void Execute(GeneratorExecutionContext context) { if (_cachedOutputDir == null) { var baseTree = context.Compilation.SyntaxTrees.FirstOrDefault(x => x.HasCompilationUnitRoot); if (baseTree == null || string.IsNullOrEmpty(baseTree.FilePath)) { context.ReportDiagnostic(Diagnostic.Create( new DiagnosticDescriptor("TSGEN001", "Missing Source File", "No valid source file found to determine project path", "Generator", DiagnosticSeverity.Warning, true), Location.None)); return; } var projectDir = Path.GetDirectoryName(baseTree.FilePath); _cachedOutputDir = Path.Combine(projectDir!, "tsFiles"); // 一次性创建目录,无需每次判断存在性 Directory.CreateDirectory(_cachedOutputDir); } // 写入TS文件逻辑 var tsPath = Path.Combine(_cachedOutputDir, "Generated.ts"); File.WriteAllText(tsPath, "// Generated content"); } public void Initialize(GeneratorInitializationContext context) {} }
优势:
- 仅第一次
Execute时计算路径和创建目录,后续复用缓存,减少性能损耗 - 添加空值判断和诊断输出,提升可靠性
3. 使用增量生成器(.NET 6+)
如果目标框架是.NET 6及以上,推荐使用增量生成器,它只会在相关代码变化时执行生成逻辑,大幅减少重复执行次数:
[Generator] public class TsIncrementalGenerator : IIncrementalGenerator { private string? _tsOutputDir; public void Initialize(IncrementalGeneratorInitializationContext context) { // 尝试从MSBuild属性获取路径 if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue( "build_property.TsOutputDir", out var outputDir)) { _tsOutputDir = outputDir; Directory.CreateDirectory(_tsOutputDir); } // 定义增量管道:仅当编译变化且路径未缓存时计算路径 var outputDirProvider = context.CompilationProvider .Select((compilation, _) => { if (_tsOutputDir != null) return _tsOutputDir; var baseTree = compilation.SyntaxTrees.FirstOrDefault(x => x.HasCompilationUnitRoot); if (baseTree == null) return null; var projectDir = Path.GetDirectoryName(baseTree.FilePath); _tsOutputDir = Path.Combine(projectDir!, "tsFiles"); Directory.CreateDirectory(_tsOutputDir); return _tsOutputDir; }) .Where(dir => dir != null); // 结合需要生成TS的语法节点(示例:所有类声明) var classDeclarations = context.SyntaxProvider .CreateSyntaxProvider( predicate: (node, _) => node is ClassDeclarationSyntax, transform: (ctx, _) => ctx.Node as ClassDeclarationSyntax) .Where(cls => cls != null); // 合并路径和类声明,生成TS文件 var generateFiles = outputDirProvider.Combine(classDeclarations.Collect()) .Select((tuple, _) => { var dir = tuple.Left!; var classes = tuple.Right!; foreach (var cls in classes) { var tsContent = $"export class {cls.Identifier.Text} {{}}"; var tsPath = Path.Combine(dir, $"{cls.Identifier.Text}.ts"); File.WriteAllText(tsPath, tsContent); } return null; }); // 注册输出 context.RegisterSourceOutput(generateFiles, (ctx, _) => {}); } }
优势:
- 仅在相关语法节点或编译环境变化时执行生成,性能最优
- 天然支持路径缓存,避免重复计算
内容的提问来源于stack exchange,提问作者AliSalehi
相关产品推荐
相关产品推荐

