源生成器是一款功能强大的工具,允许开发人员在编译过程中自动生成代码。它有助于减少样板代码、提高性能并启用新的编码模式。在本文中,我们将演示如何创建自定义源生成器,以便根据给定方法集的特定条件生成代码。
什么是源生成器?
源生成器是在编译期间运行的一段代码,它可以检查您的程序以生成与其余代码一起编译的附加文件。
源生成器如何工作?
在编译过程中,源生成器会分析代码和元数据,并基于这些分析生成额外的源代码。这些代码就像编译器的插件,在编译过程中执行。当编译器加载源生成器时,它会分析代码和元数据,以识别与源生成器相关的语法节点和语义信息。
源生成器会分析语法节点,并根据分析结果生成附加源代码。生成的代码会添加到编译中,就像开发人员编写的一样。编译完成后,生成的代码会包含在程序集中。
何时使用源生成器?
源生成器非常适合需要自动执行重复性任务、强制执行编码模式或根据代码库中的特定条件生成代码的情况。以下是源生成器特别有用的一些情况:
1、减少样板代码:如果您发现自己一遍又一遍地编写相同的代码,源代码生成器可以自动为您生成该代码。这可以节省时间并降低手动代码重复导致错误的可能性。
2、提升性能:源生成器可以生成针对特定场景(例如数据序列化或网络通信)优化的代码。这可以通过减少执行这些操作所需的开销来帮助提高应用程序的性能。
3、启用新的编码模式:源生成器可用于启用新的编码模式,例如编译时依赖注入或面向方面编程。这可以帮助开发人员编写更模块化、更易于维护且更易于测试和调试的代码。
4、适应不断变化的需求:源生成器可用于使代码适应不断变化的需求,例如外部 API 的变更或业务逻辑的变更。这有助于减少所需的手动代码更新量,并提高代码库的整体灵活性。
5、生成横切关注点的代码:源生成器可用于生成横切关注点的代码,例如日志记录、错误处理或性能监控。这有助于确保这些问题在整个应用程序中得到一致处理,并降低手动编码导致错误的风险。
了解源生成器的组件
源生成器由两个主要组件组成,它们协同工作以提供无缝且高效的代码生成过程:
ISyntaxReceiver和ISourceGenerator。通过了解这些组件及其关系,您可以根据特定需求创建自定义的源生成器。
ISyntaxReceiver负责在编译过程中收集语法节点,并根据特定条件进行过滤。ISourceGenerator负责根据ISyntaxReceiver收集到的过滤后的语法节点生成代码。
这两个组件协同工作如下:
1、ISyntaxReceiver在编译过程中收集并过滤语法节点。
2、ISourceGenerator从ISyntaxReceiver接收过滤后的语法节点。
3、ISourceGenerator根据过滤后的语法节点生成代码。
您也可以直接在 ISourceGenerator 的 Execute 方法中过滤语法节点,但使用 ISyntaxReceiver 更高效,因为它允许您在编译过程中过滤语法节点,从而避免不必要的开销。
逐步创建自定义源生成器
1、设置源生成器项目
创建一个新的类库(.NET Standard)项目,目标是netstandard2.0。要使用源生成器,您需要将以下 NuGet 包添加到您的项目中:
- Microsoft.CodeAnalysis.CSharp
- Microsoft.CodeAnalysis.Analyzers
2、实现自定义语法接收器
public class CustomSyntaxReceiver : ISyntaxReceiver
{
public List<MethodDeclarationSyntax> CandidateMethods { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is MethodDeclarationSyntax methodDeclaration &&
methodDeclaration.AttributeLists.Any(al => al.Attributes.Any(a => a.Name.ToString() == "MyCustomAttribute")))
{
CandidateMethods.Add(methodDeclaration);
}
}
}
3.实现自定义源生成器
[Generator]
public class CustomSourceGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new CustomSyntaxReceiver());
}
//Inside the `Execute` method, retrieve the SyntaxReceiver and generate code based on the filtered methods. Here's a simple example that generates a static class with a method that prints the names of the filtered methods:
public void Execute(GeneratorExecutionContext context)
{
if (context.SyntaxReceiver is not CustomSyntaxReceiver receiver)
return;
var sourceBuilder = new StringBuilder();
sourceBuilder.AppendLine("using System;");
sourceBuilder.AppendLine("namespace GeneratedCode");
sourceBuilder.AppendLine("{");
sourceBuilder.AppendLine(" public static class GeneratedMethodsInfo");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine(" public static void PrintMethodNames()");
sourceBuilder.AppendLine(" {");
foreach (var method in receiver.CandidateMethods)
{
sourceBuilder.AppendLine($" Console.WriteLine(\"{method.Identifier}\");");
}
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine("}");
context.AddSource("GeneratedMethodsInfo", sourceBuilder.ToString());
}
}
4.从项目中添加源生成器作为分析器
<!-- replacing paths and names appropriately -->
<ItemGroup>
<ProjectReference Include="path-to-sourcegenerator-project.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
通过实现自定义的 ISyntaxReceiver 和 ISourceGenerator,您可以创建一个强大而高效的 Source Generator,根据特定条件生成代码。使用 ISyntaxReceiver 允许您在编译过程中过滤语法节点,从而减少开销并提高性能。
总而言之,了解源生成器的各个组件及其关系对于创建符合您特定需求的自定义源生成器至关重要。通过利用 ISyntaxReceivers 和 ISourceGenerators 的强大功能,您可以增强开发流程,并充分利用 C# 中源生成器提供的优势。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。