1. SourceGenerator与partial范式解析
在C# 9.0引入的Source Generator技术彻底改变了代码生成的方式。与传统的T4模板或运行时反射不同,Source Generator在编译期间直接分析代码结构并生成新的源代码文件。这种技术特别适合与partial类结合使用,形成一种强大的开发范式。
1.1 partial类的设计哲学
partial关键字允许我们将一个类的定义分散在多个文件中。这种设计最初是为了方便IDE生成的代码与手写代码分离。但在Source Generator场景下,它演变成了一种强大的扩展机制:
csharp复制// 用户手写部分
public partial class UserModel
{
public string Name { get; set; }
}
// 生成器生成部分
public partial class UserModel
{
public void Validate()
{
if(string.IsNullOrEmpty(Name))
throw new ArgumentException(nameof(Name));
}
}
这种模式的关键优势在于:
- 完全类型安全:生成的代码与手写代码属于同一类型系统
- 无反射开销:所有方法调用都是直接的
- 编译时验证:错误在编译阶段就能发现
1.2 Source Generator工作流程
一个典型的生成器实现需要继承自ISourceGenerator接口:
csharp复制[Generator]
public class ModelValidatorGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new ModelSyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
if(context.SyntaxReceiver is not ModelSyntaxReceiver receiver)
return;
// 分析语法树并生成代码
var code = BuildGeneratedCode(receiver.CandidateClasses);
context.AddSource("Models.Validators.g.cs", code);
}
}
生成器的工作分为两个阶段:
- 初始化阶段注册语法接收器
- 执行阶段分析代码并生成新源文件
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 测试策略深度探讨
Source Generator项目的测试需要特殊考虑,因为它的执行依赖于编译管道。微软提供了Microsoft.CodeAnalysis.Testing包来支持这类测试。
2.1 基础测试方案
csharp复制[Test]
public async Task ShouldGenerateValidator()
{
const string source = @"
public partial class User
{
public string Name { get; set; }
}";
var compilation = CreateCompilation(source);
var generator = new ModelValidatorGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
driver = driver.RunGenerators(compilation);
GeneratorDriverRunResult runResult = driver.GetRunResult();
Assert.That(runResult.GeneratedTrees, Has.Length.EqualTo(1));
Assert.That(runResult.Diagnostics, Is.Empty);
}
2.2 增量生成测试
Source Generator支持增量生成,测试时需要验证缓存行为:
csharp复制[Test]
public async Task ShouldUseIncrementalGeneration()
{
var source1 = "public partial class A {}";
var source2 = "public partial class B {}";
var compilation1 = CreateCompilation(source1);
var compilation2 = CreateCompilation(source2);
var generator = new ModelValidatorGenerator();
var driver = CSharpGeneratorDriver.Create(generator);
// 首次运行
driver = driver.RunGenerators(compilation1);
var result1 = driver.GetRunResult();
// 增量运行
driver = driver.RunGenerators(compilation2);
var result2 = driver.GetRunResult();
Assert.That(result2.GeneratedTrees.Length,
Is.GreaterThan(result1.GeneratedTrees.Length));
}
3. 企业级应用模式
在企业开发中,Source Generator通常用于以下场景:
3.1 DTO增强模式
csharp复制// 原始DTO
public partial class OrderDto
{
public int Id { get; set; }
public decimal Amount { get; set; }
}
// 生成增强
public partial class OrderDto
{
public static OrderDto FromEntity(Order entity) => /*...*/;
public Order ToEntity() => /*...*/;
public void Validate() => /*...*/;
}
3.2 API客户端生成
通过分析Swagger/OpenAPI规范自动生成强类型客户端:
csharp复制[ApiClient]
public partial class OrderServiceClient
{
[Get("/api/orders/{id}")]
public partial Task<Order> GetOrderAsync(int id);
[Post("/api/orders")]
public partial Task CreateOrderAsync(Order order);
}
4. 性能优化技巧
4.1 缓存策略实现
csharp复制public class ModelValidatorGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var provider = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (n, _) => IsModelCandidate(n),
transform: (ctx, _) => GetModelInfo(ctx))
.Where(m => m is not null)
.WithTrackingName("ModelDiscovery");
context.RegisterSourceOutput(provider, (ctx, model) =>
{
var code = GenerateValidator(model);
ctx.AddSource($"{model.Name}.g.cs", code);
});
}
}
4.2 并行处理优化
csharp复制public void Execute(GeneratorExecutionContext context)
{
var options = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount - 1
};
Parallel.ForEach(models, options, model =>
{
var code = GenerateCode(model);
lock(context)
{
context.AddSource($"{model.Name}.g.cs", code);
}
});
}
5. 常见问题排查
5.1 生成代码不可见
症状:生成器运行但没有看到新文件
解决方案:
- 检查obj/Debug/netX.X目录下的.g.cs文件
- 确保项目文件包含:
xml复制<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
5.2 类型解析失败
症状:生成器报错找不到类型
解决方案:
- 确保context.Compilation.References包含所需程序集
- 显式添加引用:
csharp复制var assemblyPath = typeof(SomeType).Assembly.Location;
context.Compilation = context.Compilation.AddReferences(
MetadataReference.CreateFromFile(assemblyPath));
6. 现代开发范式演进
Source Generator代表了一种新的元编程范式,与以下技术趋势密切相关:
6.1 编译时AOP
取代传统基于动态代理的AOP:
csharp复制public partial class OrderService
{
[Log]
[Transaction]
public partial void PlaceOrder(Order order);
}
6.2 领域特定语言(DSL)
开发嵌入式DSL:
csharp复制public partial class OrderWorkflow
{
[Workflow]
public static partial void ProcessOrder(Order order)
{
Validate(order);
Approve(order);
Ship(order);
}
}
6.3 响应式编程增强
自动生成Observable包装:
csharp复制public partial class UserProfile
{
[Observable]
public string Name { get; set; }
// 生成
public IObservable<string> WhenNameChanged => /*...*/;
}
7. 工具链集成
7.1 调试技巧
在生成器中设置调试断点:
- 在生成器项目添加Debugger.Launch()
- 在VS中设置"调试源生成器"选项
- 使用Diagnostics输出调试信息:
csharp复制context.ReportDiagnostic(Diagnostic.Create(
descriptor: new DiagnosticDescriptor(
id: "SG001",
title: "Debug Info",
messageFormat: "Processing {0}",
category: "Debug",
DiagnosticSeverity.Info,
isEnabledByDefault: true),
location: Location.None,
messageArgs: model.Name));
7.2 性能分析
使用Roslyn内置分析:
csharp复制var stopwatch = Stopwatch.StartNew();
// 生成代码...
stopwatch.Stop();
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"SGPERF",
"Generator Performance",
$"Generation took {stopwatch.ElapsedMilliseconds}ms",
"Performance",
DiagnosticSeverity.Info,
true),
Location.None));
8. 架构设计建议
8.1 分层生成策略
推荐的分层结构:
- 基础层:属性/方法生成
- 业务层:领域逻辑生成
- 集成层:API/序列化代码生成
8.2 元数据驱动设计
使用特性驱动代码生成:
csharp复制[GenerateDto]
public class Order
{
[Key]
public int Id { get; set; }
[Required]
public string Customer { get; set; }
}
8.3 多目标生成
支持不同平台生成不同代码:
csharp复制public void Execute(GeneratorExecutionContext context)
{
var isWeb = context.Compilation.GetTypeByMetadataName(
"Microsoft.AspNetCore.Mvc.Controller") != null;
var code = isWeb ? GenerateWebCode() : GenerateLibCode();
// ...
}
9. 前沿探索方向
9.1 AI辅助生成
结合LLM实现智能生成:
csharp复制var prompt = $"""
根据以下类定义生成验证逻辑:
{classDeclaration}
要求:
- 属性非空检查
- 字符串长度验证
- 数值范围验证
""";
var generatedCode = await _llmService.GetCompletionAsync(prompt);
9.2 可视化设计器集成
创建设计时可视化工具:
- 开发VS扩展提供设计界面
- 将设计结果保存为.genconfig文件
- 生成器读取配置生成代码
9.3 分布式生成
大型项目中的优化:
- 将代码分析任务分布到多台机器
- 使用缓存服务器存储中间结果
- 合并最终生成结果
10. 团队协作规范
10.1 版本控制策略
推荐.gitignore配置:
code复制# Source Generator
**/Generated/
**/*.g.cs
!**/obj/**/*.g.cs
10.2 代码审查要点
审查生成代码时需要检查:
- 生成代码的风格一致性
- 潜在的性能问题
- 与手写代码的边界清晰度
- 错误处理完整性
10.3 文档规范
生成器项目应包含:
- 架构决策记录(ADR)
- 示例用例文档
- 版本变更日志
- 贡献者指南
在大型项目中采用Source Generator需要建立明确的代码所有权边界。我们团队的经验是:生成代码应该像第三方库一样对待 - 使用但不修改。任何必要的调整都应该通过生成器的配置或扩展点来实现,而不是直接编辑生成结果
