1. SourceGenerator与partial范式解析
在C# 9.0引入的Source Generator技术彻底改变了代码生成的方式,它允许开发者在编译期间动态生成代码文件。与传统的T4模板或运行时反射不同,Source Generator直接在编译流水线中运作,这意味着:
- 零运行时开销
- 完美的IDE智能感知支持
- 与编译过程深度集成
partial类是这个技术的最佳搭档。通过将自动生成代码与手写代码分离到不同的partial类定义中,我们实现了:
csharp复制// 自动生成部分
[GeneratedCode("UserGenerator")]
partial class User {
public string GeneratedProperty { get; set; }
}
// 手写部分
partial class User {
public void CustomMethod() {
// 业务逻辑
}
}
这种范式特别适合:
- DTO对象的自动生成
- API客户端代码构建
- 重复性样板代码的自动化
2. 实战:构建用户模型生成器
2.1 项目基础配置
首先创建标准的.NET类库项目,添加必要的NuGet包引用:
xml复制<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
</ItemGroup>
2.2 核心生成器实现
创建继承自ISourceGenerator的生成器核心类:
csharp复制[Generator]
public class UserModelGenerator : ISourceGenerator {
public void Initialize(GeneratorInitializationContext context) {
context.RegisterForSyntaxNotifications(() =>
new UserModelSyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context) {
if (context.SyntaxReceiver is not UserModelSyntaxReceiver receiver)
return;
// 代码生成逻辑
var source = GenerateUserClasses(receiver.CandidateClasses);
context.AddSource("UserModels.g.cs", SourceText.From(source, Encoding.UTF8));
}
}
2.3 语法接收器设计
语法接收器用于在编译过程中收集需要处理的语法节点:
csharp复制class UserModelSyntaxReceiver : ISyntaxReceiver {
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode) {
if (syntaxNode is ClassDeclarationSyntax classDecl &&
classDecl.AttributeLists.Count > 0) {
CandidateClasses.Add(classDecl);
}
}
}
3. 测试策略与实施
3.1 单元测试框架选择
推荐使用xUnit+Verify的组合:
csharp复制[UsesVerify]
public class UserGeneratorTests {
[Fact]
public Task ShouldGenerateCorrectUserModel() {
var source = """
[GenerateUserModel]
partial class User {}
""";
var result = TestHelper.GetGeneratedOutput(source);
return Verifier.Verify(result);
}
}
3.2 测试辅助工具类
构建测试基础设施:
csharp复制static class TestHelper {
public static string GetGeneratedOutput(string sourceCode) {
var (compilation, diagnostics) = GetCompilation(sourceCode);
GeneratorDriver driver = CSharpGeneratorDriver
.Create(new UserModelGenerator());
driver.RunGeneratorsAndUpdateCompilation(
compilation,
out var outputCompilation,
out var generateDiagnostics);
return outputCompilation.SyntaxTrees
.Last()
.ToString();
}
}
3.3 增量生成测试
验证增量编译场景下的正确性:
csharp复制[Fact]
public void ShouldSupportIncrementalGeneration() {
var source1 = "[GenerateUserModel] partial class User {}";
var source2 = "[GenerateUserModel] partial class Product {}";
var driver = CSharpGeneratorDriver.Create(new UserModelGenerator());
var compilation1 = GetCompilation(source1).Compilation;
driver = driver.RunGenerators(compilation1);
var compilation2 = GetCompilation(source2).Compilation;
driver = driver.RunGenerators(compilation2);
var runResult = driver.GetRunResult();
Assert.Equal(2, runResult.GeneratedTrees.Length);
}
4. 高级应用场景
4.1 多文件协同生成
处理复杂对象关系:
csharp复制void GenerateRelatedModels(List<ClassDeclarationSyntax> classes) {
var graph = BuildDependencyGraph(classes);
foreach (var node in TopologicalSort(graph)) {
GenerateModelWithDependencies(node);
}
}
4.2 性能优化技巧
- 使用Symbol而不是Syntax进行类型检查
- 实现IIncrementalGenerator提升增量编译性能
- 采用缓存机制避免重复分析
4.3 错误处理与诊断
生成有意义的编译错误:
csharp复制context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"SG001",
"Invalid model definition",
"Class {0} must be partial",
"Design",
DiagnosticSeverity.Error,
true),
Location.None,
classSymbol.Name));
5. 调试与问题排查
5.1 调试器附加技术
在generator项目中添加调试钩子:
json复制"launchSettings.json": {
"profiles": {
"Debug Generator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../ConsumerProject/ConsumerProject.csproj"
}
}
}
5.2 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 代码未生成 | 未正确注册生成器 | 检查[Generator]属性 |
| 智能感知缺失 | 未实现ISourceGenerator | 确认接口实现 |
| 编译循环 | 生成代码触发自身 | 添加生成条件判断 |
5.3 性能分析工具
使用Roslyn的API分析生成耗时:
csharp复制var stopwatch = Stopwatch.StartNew();
// 生成代码...
context.ReportDiagnostic(
Diagnostic.Create(
new DiagnosticDescriptor(
"SGPERF",
"Generation time",
$"Generation took {stopwatch.ElapsedMilliseconds}ms",
"Performance",
DiagnosticSeverity.Info,
false),
Location.None));
6. 架构设计建议
6.1 分层设计模式
推荐的三层架构:
- 语法分析层:负责识别代码模式
- 模型构建层:创建中间表示
- 代码生成层:输出最终代码
6.2 元数据处理
处理编译时提供的额外信息:
csharp复制var additionalFiles = context.AdditionalFiles;
foreach (var file in additionalFiles) {
var content = file.GetText(context.CancellationToken);
// 处理外部配置
}
6.3 多目标框架支持
通过条件编译适应不同平台:
csharp复制#if NETSTANDARD2_0
// 兼容性实现
#else
// 最新API实现
#endif
7. 生产环境实践
7.1 CI/CD集成
在构建管道中添加生成器验证步骤:
yaml复制- name: Validate Source Generation
run: dotnet build --no-incremental /p:ValidateSourceGenerators=true
7.2 版本控制策略
建议的版本号规则:
- 主版本:生成器重大架构变更
- 次版本:新增生成功能
- 修订号:问题修复和优化
7.3 性能监控指标
关键监控点:
- 生成器初始化时间
- 单文件生成耗时
- 内存占用峰值
8. 生态整合
8.1 与ASP.NET Core集成
自动生成控制器:
csharp复制void GenerateControllers(GeneratorExecutionContext context) {
var models = GetApplicationModels(context);
foreach (var model in models) {
var controllerCode = $$"""
[ApiController]
[Route("api/[controller]")]
public class {{model.Name}}Controller : ControllerBase {
[HttpGet]
public IActionResult Get() => Ok(Array.Empty<{{model.Name}}>());
}
""";
context.AddSource($"{model.Name}Controller.g.cs",
SourceText.From(controllerCode, Encoding.UTF8));
}
}
8.2 与EF Core配合
自动生成实体配置:
csharp复制void GenerateEntityConfigurations() {
var code = new StringBuilder();
foreach (var property in GetEntityProperties()) {
code.AppendLine($"builder.Property(x => x.{property.Name})");
if (property.IsKey) {
code.AppendLine(" .IsKey()");
}
// 更多配置...
}
}
8.3 与Blazor协同
生成Razor组件:
csharp复制void GenerateComponents() {
var componentCode = """
<h3>@Title</h3>
@code {
[Parameter]
public string Title { get; set; }
}
""";
context.AddSource("GeneratedComponent.razor",
SourceText.From(componentCode, Encoding.UTF8));
}
