编辑
2025-09-22
C#
00

目录

🔍 问题分析:为什么需要源生成器?
💡 解决方案:Source Generator核心机制
🔧 代码实战:构建你的第一个源生成器
📁 项目结构设置
🎯 核心生成器代码
🎮 使用生成的代码
📈 实际应用场景
⚠️ 常见坑点提醒
🔥 进阶技巧
🎯 总结与展望

你是否曾经为了重复的代码模板而感到厌烦?是否想过让编译器帮你自动生成代码?今天我要分享一个C#开发中的"黑科技"——源生成器(Source Generator)。这项技术可以在编译时自动生成代码,不仅能提升开发效率,还能减少运行时反射的性能开销。本文将通过一个完整的实战案例,带你掌握这项强大的技术。

🔍 问题分析:为什么需要源生成器?

在日常C#开发中,我们经常遇到这些痛点:

重复代码问题:比如属性通知、序列化代码、API接口包装等,大量模板化代码需要手写。

运行时反射开销:传统的代码生成依赖反射,会带来性能损耗。

编译时安全性:动态生成的代码缺乏编译时检查,容易出现运行时错误。

代码维护困难:手写的模板代码增加了维护成本。

源生成器完美解决了这些问题,它在编译时分析你的代码,然后自动生成需要的代码片段,既保证了性能,又提供了编译时安全性。

💡 解决方案:Source Generator核心机制

源生成器基于Roslyn编译器平台,工作流程如下:

  1. 编译时触发:在编译过程中,生成器被调用
  2. 语法分析:分析现有代码的语法树
  3. 代码生成:根据分析结果生成新的代码文件
  4. 注入编译:将生成的代码加入到编译过程中

这种机制的最大优势是零运行时开销,生成的代码就像手写代码一样高效。

🔧 代码实战:构建你的第一个源生成器

让我们通过实际代码来理解源生成器的工作原理。

📁 项目结构设置

首先创建两个项目:

生成器项目(AppSourceGeneratorLibrary.csproj)

XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <IncludeBuildOutput>false</IncludeBuildOutput> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> </ItemGroup> </Project>

关键配置说明

  • netstandard2.0:确保兼容性(必须的)
  • IncludeBuildOutput>false:不将生成器dll包含到输出中
  • PrivateAssets="all":避免传递依赖

使用项目(AppSourceGenerator.csproj)

XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\AppSourceGeneratorLibrary\AppSourceGeneratorLibrary.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup> </Project>

核心配置

  • OutputItemType="Analyzer":将项目作为分析器引用
  • ReferenceOutputAssembly="false":不引用生成器的程序集

🎯 核心生成器代码

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis; #pragma warning disable RS1035 namespace AppSourceGeneratorLibrary { [Generator] public class HelloWorldGenerator : ISourceGenerator { public void Execute(GeneratorExecutionContext context) { // 🔥 代码生成的核心逻辑 StringBuilder sourceBuilder = new StringBuilder(@" using System; namespace HelloWorldGenerated { public static class HelloWorld { public static void SayHello() { Console.WriteLine(""Hello 代码生成了""); "); // 📊 分析编译中的所有语法树 IEnumerable<SyntaxTree> syntaxTrees = context.Compilation.SyntaxTrees; // 🔄 动态添加文件路径信息 foreach (SyntaxTree tree in syntaxTrees) { sourceBuilder.AppendLine($@"Console.WriteLine(@"" - {tree.FilePath}"");"); } sourceBuilder.Append(@" } } }"); // ✨ 将生成的代码注入到编译过程 context.AddSource("helloWorldGenerated.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); } public void Initialize(GeneratorInitializationContext context) { // 这里可以注册语法接收器等高级功能 } } }

🎮 使用生成的代码

C#
using HelloWorldGenerated; namespace AppSourceGenerator { class Program { static void Main() { HelloWorld.SayHello(); // 调用生成器创建的方法 } } }

image.png

📈 实际应用场景

源生成器在实际项目中有广泛应用:

1. 属性通知生成:自动为ViewModels生成INotifyPropertyChanged实现

2. 序列化代码:为类自动生成JSON序列化代码

3. 映射代码:自动生成对象映射逻辑

4. API客户端:根据OpenAPI规范自动生成HTTP客户端

5. 配置绑定:自动生成强类型配置类

⚠️ 常见坑点提醒

性能陷阱

  • 生成器会在每次编译时执行,避免复杂的IO操作
  • 使用StringBuilder而非字符串拼接来构建代码

调试困难

  • 生成的代码默认不显示,可以通过设置<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>来查看
  • 使用System.Diagnostics.Debugger.Launch()来调试生成器

版本兼容性

  • 确保使用正确的Roslyn版本
  • 测试不同.NET版本的兼容性

🔥 进阶技巧

语法接收器使用

C#
public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new CustomSyntaxReceiver()); }

增量生成:利用IIncrementalGenerator接口提升大型项目的编译性能。

属性标记:结合特性来控制代码生成逻辑,实现更精确的控制。

🎯 总结与展望

源生成器是现代C#开发的重要工具,它通过编译时代码生成解决了传统反射的性能问题。掌握这项技术,你可以:

提升开发效率:自动化重复代码的编写

优化运行性能:消除反射带来的运行时开销

增强代码质量:提供编译时安全检查

随着.NET生态的发展,源生成器将在更多场景中发挥重要作用。建议你在项目中逐步引入这项技术,从简单场景开始,逐步扩展到复杂应用。


💬 互动话题:你在项目中遇到过哪些重复代码的场景?是否考虑过用源生成器来解决?欢迎在评论区分享你的经验和想法!

觉得这篇文章对你有帮助吗?请转发给更多需要的C#开发同行! 🚀

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!