编辑
2025-09-22
C#
00

目录

🎯 什么是Source Generators?
💡 与传统代码生成的区别
🔧 实战:构建你的第一个Source Generator
📋 项目结构设计
🎪 定义标记特性
🔍 实现核心生成器
🧪 测试生成器效果
🛡️ 开发中的常见坑点
⚠️ 性能陷阱
🔧 调试技巧
📦 部署注意事项
🎁 进阶应用场景
🔥 自动DTO映射
📊 性能计数器生成
🌐 API客户端生成
🎯 总结
💬 互动时间

还在为写重复代码而烦恼?还在因为手动维护样板代码而加班?如果你是一位C#开发者,那么今天要分享的这个编译时黑科技绝对会让你眼前一亮!

C# 9.0引入的Source Generators不仅能够自动生成代码,还能在编译时就完成所有工作,零运行时开销!想象一下,只需要加个特性标记,编译器就能自动为你生成所需的代码,这种开发体验简直不要太爽!

本文将从实战角度带你掌握Source Generators的核心技术,让你的C#开发更加高效和优雅。

🎯 什么是Source Generators?

Source Generators是基于Roslyn编译器平台的编译时代码生成器,它具有两个核心能力:

  1. 代码分析:获取编译过程中的所有用户代码,包括语法树和语义模型
  2. 代码生成:向编译过程动态添加C#源文件

💡 与传统代码生成的区别

相比T4模板或反射等传统方式,Source Generators有着显著优势:

  • 🚀 零运行时开销:在编译时生成,不影响应用性能
  • 📁 无文件污染:只在内存中添加代码,不会创建物理文件
  • 🔧 完美集成:与Visual Studio、MSBuild、dotnet CLI无缝配合

🔧 实战:构建你的第一个Source Generator

让我们通过一个实际例子来掌握Source Generators的开发流程。

📋 项目结构设计

C#
MyGenerator(解决方案) ├── AppMyGenerator.csproj (类库, netstandard2.0) │ ├── GenerateDateTimeAttribute.cs │ └── GenerateDateTimeAttribute.cs └── AppMyGeneratorTest.csproj
XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>9.0</LangVersion> <IncludeBuildOutput>false</IncludeBuildOutput> <GeneratePackageOnBuild>false</GeneratePackageOnBuild> <DevelopmentDependency>true</DevelopmentDependency> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> </ItemGroup> </Project>

🎪 定义标记特性

首先创建一个用于标记目标类的特性:

C#
using System; namespace AppMyGenerator { [AttributeUsage(AttributeTargets.Class)] public class GenerateDateTimeAttribute : Attribute { } }

🔍 实现核心生成器

创建实现ISourceGenerator接口的生成器类:

C#
using System; using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis; using System.Linq; namespace AppMyGenerator { [Generator] public class MyGeneratorClass : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); } public void Execute(GeneratorExecutionContext context) { var compilation = context.Compilation; var receiver = (SyntaxReceiver)context.SyntaxReceiver; if (receiver == null) return; foreach (var classDeclaration in receiver.CandidateClasses) { var model = compilation.GetSemanticModel(classDeclaration.SyntaxTree); var classSymbol = model.GetDeclaredSymbol(classDeclaration); if (classSymbol == null) continue; var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); var originalClassName = classSymbol.Name; // 生成源代码 - 扩展原类 var source = GenerateClassSource(originalClassName, namespaceName); context.AddSource($"{originalClassName}_Generated.cs", SourceText.From(source, Encoding.UTF8)); } } private string GenerateClassSource(string className, string namespaceName) { var sourceBuilder = new StringBuilder(); sourceBuilder.AppendLine("using System;"); sourceBuilder.AppendLine(); sourceBuilder.AppendLine($"namespace {namespaceName}"); sourceBuilder.AppendLine("{"); sourceBuilder.AppendLine($"\tpublic partial class {className}"); sourceBuilder.AppendLine("\t{"); sourceBuilder.AppendLine("\t\tpublic static string CurrentDateTime => DateTime.Now.ToString();"); sourceBuilder.AppendLine("\t}"); sourceBuilder.AppendLine("}"); return sourceBuilder.ToString(); } } internal class SyntaxReceiver : ISyntaxReceiver { public List<ClassDeclarationSyntax> CandidateClasses { get; } = new(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is not ClassDeclarationSyntax classDeclaration || classDeclaration.AttributeLists.Count <= 0) return; foreach (var attributeList in classDeclaration.AttributeLists) { var hasAttribute = attributeList.Attributes .Select(attr => attr.Name.ToString()) .Any(name => name is "GenerateDateTime" or "GenerateDateTimeAttribute"); if (hasAttribute) { CandidateClasses.Add(classDeclaration); break; } } } } }

🧪 测试生成器效果

创建测试项目来验证生成器:

XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <LangVersion>9.0</LangVersion> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\AppMyGenerator\AppMyGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" /> </ItemGroup> </Project>
C#
using System; using AppMyGenerator; namespace AppMyGeneratorTest { [GenerateDateTime] public partial class Program { static void Main(string[] args) { // 现在可以直接使用生成的方法 Console.WriteLine($"Hello, world! 当前时间: {Program.CurrentDateTime}"); Console.WriteLine("Done"); Console.ReadLine(); } } }

运行结果:

image.png

🛡️ 开发中的常见坑点

⚠️ 性能陷阱

  • 避免在Execute方法中进行重复计算
  • 合理使用缓存机制
  • 注意内存使用,及时释放不需要的对象

🔧 调试技巧

  • 使用#if DEBUG条件编译来添加调试信息
  • 利用context.ReportDiagnostic输出诊断信息
  • 在Visual Studio中启用"显示所有文件"查看生成的代码

📦 部署注意事项

🎁 进阶应用场景

Source Generators的威力远不止于此,以下是一些高级应用场景

🔥 自动DTO映射

C#
[AutoMap(typeof(UserEntity))] public class UserDto { // 自动生成映射方法 }

📊 性能计数器生成

C#
[GeneratePerformanceCounters] public class ApiController { // 自动生成性能监控代码 }

🌐 API客户端生成

C#
[GenerateApiClient("https://api.example.com/swagger.json")] public partial class ApiClient { // 根据Swagger文档自动生成客户端方法 }

🎯 总结

Source Generators作为C#编译时代码生成的革命性工具,为我们带来了三个核心价值:

  1. 🚀 开发效率提升:告别重复的样板代码编写
  2. ⚡ 零运行时开销:所有工作在编译时完成
  3. 🔧 完美工具集成:与现有开发工具无缝配合

通过今天的实战练习,相信你已经掌握了Source Generators的基础用法。从简单的属性生成到复杂的业务逻辑自动化,这个技术将成为你C#开发路上的得力助手


💬 互动时间

你在实际项目中遇到过哪些重复代码的痛点?想用Source Generators解决什么样的问题?欢迎在评论区分享你的想法和经验!

觉得这篇文章对你有帮助?别忘了转发给更多的C#同行! 让我们一起拥抱更高效的编程方式!

#C#开发 #编程技巧 #代码生成 #性能优化

本文作者:技术老小子

本文链接:

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