编译流程
Roslyn 编译流程
编译阶段详解
1. 词法分析 (Lexical Analysis)
将源代码文本分解为标记(Tokens)。
输入:
csharp
public class Person { }输出:
public(关键字)class(关键字)Person(标识符){(符号)}(符号)
2. 语法分析 (Syntax Analysis)
将标记组织成语法树。
输出:
ClassDeclaration
├── Modifiers: public
├── Keyword: class
├── Identifier: Person
└── Members: (empty)3. 语义分析 (Semantic Analysis)
进行类型检查和符号解析。
功能:
- 类型检查
- 名称解析
- 重载解析
- 类型推断
4. 源生成器执行
这是源生成器介入的时机!
5. IL 代码生成
将语义模型转换为中间语言(IL)代码。
6. 程序集生成
生成最终的 .dll 或 .exe 文件。
源生成器的位置
源生成器在语义分析之后、IL 生成之前执行:
源代码 → 语法树 → 语义模型 → [源生成器] → 新源文件 → 重新分析 → IL 代码为什么在这个位置?
- ✅ 可以访问完整的类型信息
- ✅ 可以查询符号和语义
- ✅ 生成的代码会被重新编译
- ✅ 生成的代码可以被其他代码使用
增量编译
现代编译器使用增量编译来提高性能:
增量生成器利用这一机制来优化性能。
实际示例
编译一个简单的类
csharp
public class Calculator
{
public int Add(int a, int b) => a + b;
}编译步骤:
- 词法分析 → 生成 tokens
- 语法分析 → 构建语法树
- 语义分析 → 解析类型、方法签名
- 源生成器 → (如果有)生成额外代码
- IL 生成 → 生成 IL 指令
- 程序集 → 输出 .dll
下一步
编译阶段状态转换
编译状态机
各阶段的输入输出
| 阶段 | 输入 | 输出 | 主要操作 |
|---|---|---|---|
| 词法分析 | 源代码文本 | Token 流 | 分词、识别关键字 |
| 语法分析 | Token 流 | 语法树 | 构建 AST、检查语法 |
| 语义分析 | 语法树 + 引用 | 语义模型 | 类型检查、符号解析 |
| 源生成 | 语义模型 | 新源文件 | 分析代码、生成代码 |
| IL 生成 | 语义模型 | IL 代码 | 生成中间语言 |
| 程序集生成 | IL 代码 | .dll/.exe | 输出最终文件 |
源生成器执行时机详解
详细的执行流程
源生成器的两个阶段
阶段 1: Initialize(初始化)
csharp
public void Initialize(GeneratorInitializationContext context)
{
// 1. 注册语法接收器(可选)
context.RegisterForSyntaxNotifications(() => new MySyntaxReceiver());
// 2. 注册后处理(可选)
context.RegisterForPostInitialization(ctx =>
{
// 生成全局代码,如特性定义
ctx.AddSource("Attributes.g.cs", attributeCode);
});
}执行时机:编译开始时,只执行一次
用途:
- 注册语法接收器
- 生成全局代码(如特性定义)
- 设置生成器配置
阶段 2: Execute(执行)
csharp
public void Execute(GeneratorExecutionContext context)
{
// 1. 获取编译信息
var compilation = context.Compilation;
// 2. 获取语法接收器收集的节点
if (context.SyntaxReceiver is MySyntaxReceiver receiver)
{
// 3. 处理每个候选节点
foreach (var classDecl in receiver.CandidateClasses)
{
var semanticModel = compilation.GetSemanticModel(classDecl.SyntaxTree);
var symbol = semanticModel.GetDeclaredSymbol(classDecl);
// 4. 生成代码
var code = GenerateCode(symbol);
context.AddSource($"{symbol.Name}.g.cs", code);
}
}
// 5. 报告诊断(可选)
context.ReportDiagnostic(/* ... */);
}执行时机:语义分析完成后
用途:
- 分析代码
- 生成新源文件
- 报告诊断信息
源生成器可以访问的信息
csharp
public void Execute(GeneratorExecutionContext context)
{
// ✅ 可以访问:编译信息
var compilation = context.Compilation;
// ✅ 可以访问:所有语法树
foreach (var tree in compilation.SyntaxTrees)
{
// 处理...
}
// ✅ 可以访问:语义模型
var semanticModel = compilation.GetSemanticModel(tree);
// ✅ 可以访问:符号信息
var symbol = semanticModel.GetDeclaredSymbol(node);
// ✅ 可以访问:类型信息
var typeInfo = semanticModel.GetTypeInfo(expression);
// ✅ 可以访问:引用的程序集
var references = compilation.References;
// ✅ 可以访问:分析器配置
context.AnalyzerConfigOptions.GlobalOptions
.TryGetValue("build_property.RootNamespace", out var ns);
// ❌ 不能访问:文件系统
// File.ReadAllText("config.json"); // 不可靠
// ❌ 不能访问:网络
// HttpClient.GetAsync("..."); // 不允许
// ❌ 不能修改:现有代码
// 只能添加新文件,不能修改现有文件
}调试编译过程
方法 1: 使用诊断输出
csharp
public void Execute(GeneratorExecutionContext context)
{
// 输出调试信息
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SG0001",
"Debug Info",
"Compilation has {0} syntax trees",
"SourceGenerator",
DiagnosticSeverity.Info,
true),
Location.None,
context.Compilation.SyntaxTrees.Count()));
}方法 2: 使用 Debugger.Launch()
csharp
public void Execute(GeneratorExecutionContext context)
{
#if DEBUG
if (!Debugger.IsAttached)
{
Debugger.Launch(); // 弹出调试器选择对话框
}
#endif
// 生成器逻辑...
}方法 3: 附加到编译器进程
- 在 Visual Studio 中打开生成器项目
- 在生成器代码中设置断点
- 选择 调试 → 附加到进程
- 找到
csc.exe或VBCSCompiler.exe - 附加调试器
- 在使用者项目中触发编译
方法 4: 查看生成的文件
bash
# 查看生成的文件位置
dir obj\Debug\net8.0\generated /s
# 查看生成的文件内容
type obj\Debug\net8.0\generated\MyGenerator\MyGenerator.MyGenerator\Generated.g.cs调试技巧
csharp
public void Execute(GeneratorExecutionContext context)
{
try
{
// 记录开始时间
var sw = Stopwatch.StartNew();
// 生成器逻辑
var code = GenerateCode(context);
// 记录结束时间
sw.Stop();
// 输出性能信息
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SG0002",
"Performance",
"Generation took {0}ms, generated {1} characters",
"SourceGenerator",
DiagnosticSeverity.Info,
true),
Location.None,
sw.ElapsedMilliseconds,
code.Length));
context.AddSource("Generated.g.cs", code);
}
catch (Exception ex)
{
// 捕获并报告异常
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SG0003",
"Error",
"Generation failed: {0}",
"SourceGenerator",
DiagnosticSeverity.Error,
true),
Location.None,
ex.Message));
}
}性能优化
编译性能影响
优化策略
1. 使用增量生成器
csharp
// ✅ 推荐:增量生成器
[Generator]
public class MyIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 定义增量管道
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax c &&
c.AttributeLists.Count > 0,
transform: static (ctx, _) => GetSemanticTarget(ctx))
.Where(static m => m is not null);
context.RegisterSourceOutput(classDeclarations,
static (spc, source) => Execute(source, spc));
}
}
// ❌ 避免:传统生成器
[Generator]
public class MyGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 每次编译都完全执行
foreach (var tree in context.Compilation.SyntaxTrees)
{
// 处理所有文件...
}
}
}2. 提前过滤
csharp
// ✅ 推荐:使用 SyntaxReceiver 提前过滤
class MySyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
// 只收集带有特性的类
if (syntaxNode is ClassDeclarationSyntax classDecl &&
classDecl.AttributeLists.Count > 0)
{
CandidateClasses.Add(classDecl);
}
}
}
// ❌ 避免:在 Execute 中遍历所有节点
public void Execute(GeneratorExecutionContext context)
{
foreach (var tree in context.Compilation.SyntaxTrees)
{
foreach (var node in tree.GetRoot().DescendantNodes())
{
// 处理所有节点...
}
}
}3. 缓存计算结果
csharp
// ✅ 推荐:缓存重复计算
private static readonly ConcurrentDictionary<string, string> _cache = new();
public void Execute(GeneratorExecutionContext context)
{
foreach (var classSymbol in GetClassesToGenerate(context))
{
var key = classSymbol.ToDisplayString();
var code = _cache.GetOrAdd(key, _ => GenerateCode(classSymbol));
context.AddSource($"{classSymbol.Name}.g.cs", code);
}
}4. 避免不必要的语义分析
csharp
// ✅ 推荐:先语法过滤,再语义分析
var candidates = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.Where(c => c.Modifiers.Any(SyntaxKind.PublicKeyword) &&
c.AttributeLists.Count > 0); // 语法过滤
foreach (var classDecl in candidates)
{
var symbol = semanticModel.GetDeclaredSymbol(classDecl); // 只对候选类进行语义分析
if (HasTargetAttribute(symbol))
{
GenerateCode(symbol);
}
}
// ❌ 避免:对所有类进行语义分析
foreach (var classDecl in root.DescendantNodes().OfType<ClassDeclarationSyntax>())
{
var symbol = semanticModel.GetDeclaredSymbol(classDecl); // 对所有类都进行语义分析
if (HasTargetAttribute(symbol))
{
GenerateCode(symbol);
}
}实际示例
示例 1: 跟踪编译过程
csharp
[Generator]
public class CompilationTrackerGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForPostInitialization(ctx =>
{
Log(ctx, "PostInitialization: 生成全局代码");
ctx.AddSource("CompilationInfo.g.cs", $$"""
// 编译时间: {{DateTime.Now}}
public static class CompilationInfo
{
public const string CompilationTime = "{{DateTime.Now:yyyy-MM-dd HH:mm:ss}}";
}
""");
});
}
public void Execute(GeneratorExecutionContext context)
{
Log(context, $"Execute: 开始执行,共 {context.Compilation.SyntaxTrees.Count()} 个文件");
var classes = 0;
var methods = 0;
foreach (var tree in context.Compilation.SyntaxTrees)
{
var root = tree.GetRoot();
classes += root.DescendantNodes().OfType<ClassDeclarationSyntax>().Count();
methods += root.DescendantNodes().OfType<MethodDeclarationSyntax>().Count();
}
Log(context, $"Execute: 找到 {classes} 个类,{methods} 个方法");
context.AddSource("Statistics.g.cs", $$"""
public static class Statistics
{
public const int TotalClasses = {{classes}};
public const int TotalMethods = {{methods}};
}
""");
Log(context, "Execute: 完成");
}
private void Log(GeneratorExecutionContext context, string message)
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SG0001",
"Compilation Tracker",
message,
"SourceGenerator",
DiagnosticSeverity.Info,
true),
Location.None));
}
}示例 2: 条件编译
csharp
[Generator]
public class ConditionalGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 检查编译配置
var isDebug = context.Compilation.Options.OptimizationLevel ==
OptimizationLevel.Debug;
// 检查预处理符号
var hasDebugSymbol = context.ParseOptions
.PreprocessorSymbolNames
.Contains("DEBUG");
// 根据配置生成不同的代码
var code = isDebug ? GenerateDebugCode() : GenerateReleaseCode();
context.AddSource("Conditional.g.cs", code);
}
private string GenerateDebugCode()
{
return """
public static class BuildInfo
{
public const string Configuration = "Debug";
public const bool EnableLogging = true;
public const bool EnableAssertions = true;
}
""";
}
private string GenerateReleaseCode()
{
return """
public static class BuildInfo
{
public const string Configuration = "Release";
public const bool EnableLogging = false;
public const bool EnableAssertions = false;
}
""";
}
}常见问题
Q1: 源生成器会影响编译速度吗?
A: 会有一定影响,但可以优化:
- 传统生成器:增加 5-20% 编译时间
- 增量生成器:增加 1-5% 编译时间
- 优化建议:
- 使用
IIncrementalGenerator - 使用
SyntaxReceiver提前过滤 - 缓存重复计算
- 避免复杂的语义分析
- 使用
Q2: 生成的代码什么时候被编译?
A: 立即编译:
1. 源生成器执行
2. 生成新源文件
3. 新源文件被添加到编译
4. 编译器重新进行语义分析
5. 所有代码(包括生成的)一起编译为 ILQ3: 源生成器可以生成多少文件?
A: 理论上没有限制,但建议:
- 每个类生成一个文件
- 文件名使用
.g.cs后缀 - 避免生成过多小文件
- 考虑合并相关代码
csharp
// ✅ 推荐:每个类一个文件
context.AddSource("Person.g.cs", personCode);
context.AddSource("Product.g.cs", productCode);
// ⚠️ 可以但不推荐:一个大文件
context.AddSource("AllGenerated.g.cs", allCode);
// ❌ 避免:太多小文件
for (int i = 0; i < 1000; i++)
{
context.AddSource($"File{i}.g.cs", smallCode);
}Q4: 如何处理生成器之间的依赖?
A: 源生成器按顺序执行,但不应该相互依赖:
csharp
// ❌ 不推荐:依赖其他生成器
[Generator]
public class DependentGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 假设另一个生成器已经生成了 BaseClass
// 这是不可靠的!
var code = """
public class DerivedClass : BaseClass { }
""";
context.AddSource("Derived.g.cs", code);
}
}
// ✅ 推荐:独立的生成器
[Generator]
public class IndependentGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// 生成完整的、独立的代码
var code = """
public class BaseClass { }
public class DerivedClass : BaseClass { }
""";
context.AddSource("Classes.g.cs", code);
}
}Q5: 源生成器可以读取项目文件吗?
A: 可以通过 AdditionalFiles:
xml
<!-- 在项目文件中 -->
<ItemGroup>
<AdditionalFiles Include="config.json" />
</ItemGroup>csharp
// 在生成器中
public void Execute(GeneratorExecutionContext context)
{
var configFile = context.AdditionalFiles
.FirstOrDefault(f => Path.GetFileName(f.Path) == "config.json");
if (configFile != null)
{
var content = configFile.GetText()?.ToString();
// 处理配置...
}
}下一步
现在你已经深入了解了编译流程和源生成器的执行时机!接下来可以:
深入学习
实践示例
- Hello World 示例 - 最简单的生成器
- 增量生成器 - 性能优化技术
- 诊断报告 - 错误和警告处理
- 测试调试 - 测试和调试技巧
API 参考
🔗 相关资源
深入学习
- Roslyn 基础 - 学习 Roslyn 编译器的基础知识
- 语法树 - 深入理解语法树的结构和操作
- 语义模型 - 掌握语义模型的使用方法
- 符号系统 - 了解符号系统的详细信息
- 快速开始 - 开始使用源生成器
API 参考
实战示例
最后更新: 2025-01-21
编译优化技术
增量编译
Roslyn 使用增量编译来提高编译速度:
工作原理:
- 文件指纹:计算每个文件的哈希值
- 依赖追踪:记录文件之间的依赖关系
- 缓存管理:存储编译结果
- 智能重编译:只编译变化的文件及其依赖
示例:
csharp
// 项目有 100 个文件
// 修改了 File1.cs
// 传统编译:重新编译所有 100 个文件
// 增量编译:只编译 File1.cs 和依赖它的文件(可能只有 5 个)
// 编译时间对比:
// 传统:30 秒
// 增量:3 秒
// 提升 10 倍!并行编译
Roslyn 支持并行编译多个文件:
csharp
// 配置并行编译
<PropertyGroup>
<!-- 使用所有可用 CPU 核心 -->
<MaxCpuCount>0</MaxCpuCount>
<!-- 启用共享编译 -->
<UseSharedCompilation>true</UseSharedCompilation>
<!-- 并行构建项目 -->
<BuildInParallel>true</BuildInParallel>
</PropertyGroup>性能提升:
单核编译:60 秒
4 核并行:18 秒(提升 3.3 倍)
8 核并行:12 秒(提升 5 倍)编译缓存
Roslyn 使用多级缓存策略:
缓存位置:
bash
# Windows
%LOCALAPPDATA%\Microsoft\VisualStudio\RoslynCache
# Linux/Mac
~/.local/share/Microsoft/VisualStudio/RoslynCache编译诊断和调试
查看编译详情
使用详细日志查看编译过程:
bash
# 详细输出
dotnet build -v detailed
# 诊断输出(最详细)
dotnet build -v diagnostic
# 二进制日志(推荐)
dotnet build -bl:build.binlog分析二进制日志
使用 MSBuild Structured Log Viewer:
bash
# 1. 生成二进制日志
dotnet build -bl
# 2. 下载查看器
# https://msbuildlog.com/
# 3. 打开 msbuild.binlog 文件
# 可以看到:
# - 每个任务的执行时间
# - 源生成器的执行情况
# - 编译错误和警告
# - 文件依赖关系调试编译过程
方法 1: 使用环境变量
bash
# Windows
set ROSLYN_COMPILER_DEBUG=1
dotnet build
# Linux/Mac
export ROSLYN_COMPILER_DEBUG=1
dotnet build方法 2: 附加调试器
csharp
// 在源生成器中添加
public void Execute(GeneratorExecutionContext context)
{
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
#endif
// 生成器逻辑...
}方法 3: 使用诊断输出
csharp
public void Execute(GeneratorExecutionContext context)
{
// 输出诊断信息
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SG9999",
"Debug Info",
"Compilation: {0}, SyntaxTrees: {1}",
"Debug",
DiagnosticSeverity.Info,
true),
Location.None,
context.Compilation.AssemblyName,
context.Compilation.SyntaxTrees.Count()));
}编译错误处理
常见编译错误
1. 语法错误
csharp
// 错误代码
public class Person
{
public string Name { get; set; }
// 缺少右花括号错误信息:
CS1513: } expected解决方法:添加缺失的花括号
2. 类型错误
csharp
// 错误代码
string number = 123; // 类型不匹配错误信息:
CS0029: Cannot implicitly convert type 'int' to 'string'解决方法:
csharp
string number = 123.ToString(); // 显式转换3. 命名空间错误
csharp
// 错误代码
var list = new List<string>(); // 缺少 using错误信息:
CS0246: The type or namespace name 'List<>' could not be found解决方法:
csharp
using System.Collections.Generic;
var list = new List<string>();源生成器错误
1. 生成器未执行
症状:生成的代码不存在
检查清单:
xml
<!-- 1. 检查项目引用 -->
<ItemGroup>
<ProjectReference Include="..\MyGenerator\MyGenerator.csproj"
OutputItemType="Analyzer" <!-- 必须 -->
ReferenceOutputAssembly="false" /> <!-- 必须 -->
</ItemGroup>
<!-- 2. 检查生成器特性 -->
[Generator] // 必须有此特性
public class MyGenerator : ISourceGenerator { }
<!-- 3. 清理并重建 -->
dotnet clean
dotnet build2. 生成的代码有错误
症状:编译失败,错误指向生成的代码
调试步骤:
bash
# 1. 查看生成的文件
type obj\Debug\net8.0\generated\MyGenerator\*.g.cs
# 2. 检查生成逻辑
# 在生成器中添加日志
# 3. 验证生成的代码语法
# 使用在线 C# 编译器测试3. 性能问题
症状:编译时间显著增加
优化方法:
csharp
// ✅ 使用增量生成器
[Generator]
public class MyGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 增量管道
}
}
// ✅ 使用 SyntaxReceiver 过滤
class MySyntaxReceiver : ISyntaxReceiver
{
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
// 只收集需要的节点
if (syntaxNode is ClassDeclarationSyntax classDecl &&
classDecl.AttributeLists.Count > 0)
{
// 收集...
}
}
}
// ✅ 缓存计算结果
private static readonly Dictionary<string, string> _cache = new();
public string GenerateCode(string key)
{
if (_cache.TryGetValue(key, out var cached))
{
return cached;
}
var result = /* 生成代码 */;
_cache[key] = result;
return result;
}编译配置选项
项目文件配置
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- 基本配置 -->
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<!-- 编译优化 -->
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<!-- 确定性构建 -->
<Deterministic>true</Deterministic>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<!-- 并行编译 -->
<UseSharedCompilation>true</UseSharedCompilation>
<BuildInParallel>true</BuildInParallel>
<!-- 分析器配置 -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest</AnalysisLevel>
<!-- 警告配置 -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningLevel>5</WarningLevel>
<!-- 输出配置 -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>
</Project>编译器指令
csharp
// 条件编译
#if DEBUG
Console.WriteLine("Debug mode");
#elif RELEASE
Console.WriteLine("Release mode");
#endif
// 警告控制
#pragma warning disable CS0168 // 禁用特定警告
var unused = 0;
#pragma warning restore CS0168 // 恢复警告
// 可空性控制
#nullable enable
string? nullableString = null;
#nullable disable
// 区域标记
#region Helper Methods
private void Helper() { }
#endregion
// 行号指令(用于生成的代码)
#line 100 "OriginalFile.cs"
// 错误将指向 OriginalFile.cs 的第 100 行
#line default全局配置
使用 .editorconfig 配置编译器行为:
ini
# .editorconfig
root = true
[*.cs]
# 语言版本
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
# 代码风格
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
# 格式化
csharp_new_line_before_open_brace = all
csharp_indent_case_contents = true
# 分析器严重性
dotnet_diagnostic.CA1001.severity = error
dotnet_diagnostic.CA1031.severity = warning
# 源生成器配置
build_property.MyGenerator_EnableLogging = true
build_property.MyGenerator_OutputPath = Generated编译性能分析
性能指标
关键性能指标:
1. 编译时间
- 冷编译(首次):完整编译所有文件
- 热编译(增量):只编译变化的文件
2. 内存使用
- 峰值内存:编译过程中的最大内存占用
- 平均内存:编译过程中的平均内存占用
3. CPU 使用
- CPU 时间:实际 CPU 计算时间
- 并行度:同时运行的编译任务数
4. 磁盘 I/O
- 读取:读取源文件和依赖
- 写入:写入输出文件和缓存性能测试
bash
# 1. 清理项目
dotnet clean
# 2. 冷编译测试
Measure-Command { dotnet build }
# 3. 热编译测试(修改一个文件后)
Measure-Command { dotnet build }
# 4. 并行编译测试
Measure-Command { dotnet build -m }
# 5. 生成性能报告
dotnet build -p:ReportAnalyzer=true性能优化建议
具体优化措施:
优化源生成器
csharp// ✅ 使用增量生成器 [Generator] public class MyGenerator : IIncrementalGenerator { } // ✅ 提前过滤 var candidates = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => s is ClassDeclarationSyntax, transform: static (ctx, _) => GetTarget(ctx)) .Where(static m => m is not null);优化项目结构
xml<!-- ✅ 使用项目引用而不是程序集引用 --> <ItemGroup> <ProjectReference Include="..\Shared\Shared.csproj" /> </ItemGroup> <!-- ✅ 启用引用程序集 --> <PropertyGroup> <ProduceReferenceAssembly>true</ProduceReferenceAssembly> </PropertyGroup>配置分析器
xml<!-- ✅ 只在需要时运行分析器 --> <PropertyGroup> <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild> <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis> </PropertyGroup>