1. .NET源码生成器开发概述
在.NET生态系统中,源码生成器(Source Generator)是一项强大的编译时功能,它允许开发者在编译过程中动态生成C#代码。partial类机制是与之完美配合的关键语言特性,通过将自动生成的代码与手动编写的代码分离,实现了代码生成与维护的优雅解耦。
源码生成器的核心价值在于:
- 编译时执行:不引入运行时开销
- 强类型检查:生成的代码参与完整编译过程
- 无缝集成:生成的代码与手写代码同等对待
- 开发效率:自动化重复性编码工作
重要提示:源码生成器在.NET 5+中达到生产就绪状态,建议使用最新的LTS版本以获得最佳支持。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基于partial范式的开发实践
2.1 partial类设计原则
partial类允许将单个类的定义拆分到多个文件中,这是源码生成器的理想搭档。良好的partial设计应遵循:
-
职责分离原则
- 生成部分:包含机械性、规律性强的代码
- 手动部分:包含业务逻辑和自定义实现
-
文件命名规范
bash复制User.cs # 主文件(手动代码) User.g.cs # 生成文件(自动代码) -
访问控制策略
- 生成部分通常使用
internal可见性 - 手动部分根据实际需要选择访问修饰符
- 生成部分通常使用
2.2 典型应用场景
-
DTO自动生成
csharp复制// 手动部分 public partial class OrderDto { public decimal CalculateTotal() { return Items.Sum(i => i.Price * i.Quantity); } } // 生成部分(自动) public partial class OrderDto { public int Id { get; set; } public DateTime OrderDate { get; set; } public List<OrderItemDto> Items { get; set; } } -
API客户端封装
csharp复制// 手动添加自定义方法 public partial class ApiClient { public async Task<User> GetUserWithCache(int id) { // 自定义缓存逻辑 } } -
模式实现辅助
csharp复制// 生成器实现INotifyPropertyChanged样板代码 public partial class ObservableModel : INotifyPropertyChanged { // 自动生成属性通知代码 }
3. 源码生成器实现详解
3.1 项目结构规划
推荐的标准项目结构:
code复制src/
├── MyApp/ # 主应用程序
├── MyApp.Models/ # 共享模型
└── MyApp.Generators/ # 源码生成器项目
├── Analyzers/ # 自定义诊断分析器
├── Generators/ # 生成器实现
├── Templates/ # 代码模板
└── MyApp.Generators.csproj
3.2 核心接口实现
典型的生成器骨架代码:
csharp复制[Generator]
public class DtoGenerator : ISourceGenerator {
public void Initialize(GeneratorInitializationContext context) {
// 注册语法接收器
context.RegisterForSyntaxNotifications(() => new DtoSyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context) {
if (!(context.SyntaxContextReceiver is DtoSyntaxReceiver receiver))
return;
// 处理收集到的语法节点
foreach (var classDecl in receiver.CandidateClasses) {
var model = BuildDtoModel(classDecl, context);
var source = GenerateDtoCode(model);
context.AddSource($"{model.Name}.g.cs", source);
}
}
private string GenerateDtoCode(DtoModel model) {
// 使用StringBuilder或模板引擎生成代码
}
}
3.3 增量生成优化
.NET 6+引入了增量生成器API,大幅提升性能:
csharp复制[Generator]
public class IncrementalDtoGenerator : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorInitializationContext context) {
var provider = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (n, _) => IsDtoCandidate(n),
transform: static (ctx, _) => GetDtoModel(ctx))
.Where(static m => m is not null);
context.RegisterSourceOutput(provider, (ctx, model) => {
var source = GenerateDtoCode(model!);
ctx.AddSource($"{model!.Name}.g.cs", source);
});
}
}
4. NuGet打包与分发
4.1 项目配置要点
生成器项目的csproj关键配置:
xml复制<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true"
PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
4.2 多目标框架支持
对于需要支持多种.NET版本的场景:
xml复制<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
</ItemGroup>
4.3 版本控制策略
推荐采用语义化版本控制:
- 主版本号:破坏性变更
- 次版本号:新增功能(向后兼容)
- 修订号:问题修复
nuget包配置示例:
xml复制<PropertyGroup>
<PackageVersion>1.2.3</PackageVersion>
<Version>1.2.3</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.2.3.0</FileVersion>
</PropertyGroup>
5. 高级技巧与最佳实践
5.1 调试源码生成器
-
附加调试器
csharp复制#if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); } #endif -
日志输出
csharp复制context.ReportDiagnostic(Diagnostic.Create( new DiagnosticDescriptor( "SG0001", "Info", "Generating for {0}", "Debug", DiagnosticSeverity.Info, true), Location.None, classSymbol.Name));
5.2 性能优化策略
-
缓存机制
csharp复制private static readonly ConcurrentDictionary<string, string> _cache = new(); string GetTemplate(string name) { return _cache.GetOrAdd(name, n => LoadTemplate(n)); } -
增量生成
- 使用
IIncrementalGenerator接口 - 合理设计语法过滤器
- 避免不必要的全量分析
- 使用
5.3 跨项目协作模式
-
共享模型定义
csharp复制// 在共享项目中定义生成器使用的模型 public class GenerationAttribute : Attribute { public string TemplateName { get; set; } } -
分层生成策略
- 基础层生成器:创建基础结构
- 应用层生成器:添加业务特定代码
6. 常见问题排查
6.1 生成器未执行
检查步骤:
- 确认项目引用了生成器包
- 检查生成器是否标记了
[Generator]特性 - 查看编译输出中的诊断信息
6.2 生成的代码不可见
解决方案:
- 确保文件以
.g.cs结尾 - 检查
#nullable enable是否冲突 - 验证partial类定义是否一致
6.3 NuGet包未生效
排查要点:
- 确认包路径正确(analyzers/dotnet/cs)
- 检查依赖项是否标记为PrivateAssets="all"
- 清理NuGet缓存后重试
经验分享:在实际项目中,我们发现将生成器拆分为多个小型专用生成器(而非一个大型通用生成器)能获得更好的维护性和性能表现。每个生成器专注于解决一个特定问题,通过NuGet包组合使用。
