前言
嗨,大家好!
今天我们来聊一聊 C# 里的源代码生成器,一个有趣的代码生成工具。
源代码生成器(Source Generators)是 C# 9.0 引入的一项强大功能,允许你在编译时动态生成源代码。
这意味着,你可以编写代码来自动生成其他代码,从而减少手动重复的工作。
这个过程在编译阶段发生,生成的代码会在编译输出中包含,从而使你的类库或应用程序更轻便、更可维护。
首先我们通过一个 Step By Step 例子来感受一下它的魅力吧!
Step By Step 例子
我们来创建一个源生成器,它可以自动为一个类生成一个 ToString
方法,方便快速打印统一的对象信息
1. 创建项目
在 Visual Studio 2022 IDE 中,创建一个新的类库项目,命名为 SourceGen
选择 .NET Standard 2.0 版本,如图:
2. 添加 NuGet 包
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
注意:
不要选择太高的版本,否则可能会出现编译器版本太高的问题,比如:
分析器程序集“...”引用了编译器的版本 “4.12.0.0”,该版本高于当前正在运行的版本 “4.8.0.0”
包 Microsoft.CodeAnalysis.CSharp
已经包含了 Microsoft.CodeAnalysis.Analyzers
,所以无需继续添加此包
3. 配置 EnforceExtendedAnalyzerRules 规则
打开项目文件 SourceGen.csproj
,在 PropertyGroup
节点下手动增加 EnforceExtendedAnalyzerRules
配置,如图:
4. 创建源生成器
在项目中,创建一个新的类文件,命名为 ToStringGenerator.cs
,并编写以下代码:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Text;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespaceSourceGen
{
[Generator]
publicclassToStringGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var syntaxTrees = context.Compilation.SyntaxTrees;
foreach (var tree in syntaxTrees)
{
var root = tree.GetRoot();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var classDeclaration in classes)
{
// 获取类名
var className = classDeclaration.Identifier.Text;
// 获取类成员
var properties = classDeclaration.Members.OfType<PropertyDeclarationSyntax>();
var propertiesList = string.Join(", ", properties.Select(p => $"{p.Identifier.Text} = {{{p.Identifier.Text}}}"));
// 写 ToString 方法
// 注意这里用了 partial 修饰符
var toStringMethod = $@"
public partial class {className} {{
public override string ToString() => ""{className} {{{propertiesList}}}"";
}}
";
context.AddSource($"{className}_ToString.g.cs", SourceText.From(toStringMethod, Encoding.UTF8));
}
}
}
public void Initialize(GeneratorInitializationContext context)
{
// 可以在这里初始化任何需要的对象或资源
}
}
}
编译一下确定代码没问题。
5. 创建新控制台项目
在解决方案中,添加一个新的控制台项目,命名为 SourceGeneratorSample
6. 引用源代码生成器项目 SourceGen
注意,引用后,需要打开 SourceGeneratorSample.csproj,修改项目引用节点内容,添加 OutputItemType
和 ReferenceOutputAssembly
配置:
<ProjectReference Include="..\SourceGen\SourceGen.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
7. 定义一个类
在 SourceGeneratorSample
项目中,创建一个简单的类,例如 Person
:
public partial class Person
{
public string? Name { get; set; }
public int Age { get; set; }
}
注意:
8. 使用源代码器生成的方法
在 Program.cs
文件中,创建一个 Person
实例对象并打印它的 ToString
:
var person = new Person { Name = "Alice", Age = 30 };
Console.WriteLine(person.ToString());
9. 运行
按 Ctrl+F5 编译并运行程序,你会在控制台看到如下图输出:
优势与劣势
在上面的例子里,我们为项目中所有的类统一了 ToString
方法的输出,从中我们可以看出一些源代码生成器的优势和劣势:
优势
减少重复劳动:源代码生成器可以自动生成一些繁琐的重复性代码,特别是那些不变的基本数据结构或方法,比如数据传输对象(DTO)或实体类。
提高代码一致性:生成的代码遵循预设的逻辑,可以更好地保持一致性,减少了人为错误
保持代码整洁:将生成的代码与手写的代码分开,主代码会显得更加清晰易读
劣势
编译时错误难以调试:因为代码是在编译阶段生成的,如果生成的代码有问题,定位和修复会相对比较困难
学习成本:源代码生成器的概念和使用方式跟传统开发方式差别比较大,需要一定的学习时间来掌握其使用方法
可能增加复杂性:源代码生成器的实现需要对语法树有深入的理解,这可能增加开发的复杂性。
版本兼容性:如果使用的源生成器依赖于特定版本的编译器或框架,升级时可能会有兼容性的问题
总结
随着 .NET 8 的发布,源代码生成器这一强大特性也逐渐走进了更多程序员的视野。
相比传统的 T4 模板等代码生成工具,源代码生成器的最大魅力在于它能在编译时动态生成代码。
这意味着你生成的代码能立刻融入项目中,无需反复编译,就像魔法一样——写完代码马上就能看到结果,大大提升了开发效率。
不过,任何好东西都有它的 “小脾气”,源代码生成器也不例外,有时候调试它很让人抓狂,特别是如果你对语法树不太熟悉的话,刚开始接触时,可能会觉得有点棘手,需要花点时间去适应和学习。
所以呢,要不要在你的项目里引入这个强大的特性,其实取决于具体情况。如果你的项目适合并且你能接受一点初期的学习成本,那么源代码生成器绝对会让你的开发体验焕然一新。但如果项目时间紧迫或团队成员对它还不够熟悉,可能就需要再三考虑一下啦。
该文章在 2025/1/6 10:15:30 编辑过