C#源代码生成器深入讲解二

在阅读本文前需掌握源代码生成器相关知识C#源代码生成器深入讲解一

C#源代码生成器深入讲解二—增量生成器

源代码生成器有个非常大的弊病,每次都会遍历所有的语法树来分析,这样就有个问题,每次可能只修改了很少一部分或者只有很少一部分的代码需要分析,而增量源代码生成器可以理解为在之前的工作上做了一个筛选的动作,通过自定义的条件来过滤语法树,并且缓存起来,避免在没有做任何更改的情况下重复工作,提高效率。

1 增量生成器初体验

增量生成器和源代码生成器基本方法相同,只不过只需要一个Initialize方法

  1. 新建一个生成器项目
[Generator(LanguageNames.CSharp)]
public class GreetingIncrementalGenerator : IIncrementalGenerator
{//仅仅实现一个接口public void Initialize(IncrementalGeneratorInitializationContext context){//生成源代码的操作,不会立即执行,而是在编译时执行context.RegisterPostInitializationOutput(e =>{e.AddSource($"GreetingIncrementalGenerator.g.cs", """//加上这句话,告知编译器,这个文件是由源代码生成器生成的,//防止编译器进行代码分析,避免不必要的编译器警告//<auto-generated>namespace GreetingTest;class GreetingIncrementalGreetingIncremental{public static void SayHello(string name){global::System.Console.WriteLine($"Hello, World {name}!");}}""");});}
}
  1. 使用增量生成器
GreetingIncrementalGreetingIncremental.SayHello("IncrementalGeneratorInitialization");

image-20231110170341559

2 使用CreateSyntaxProvider

上一章节中直接使用字符串进行了代码生成而没有进行语法分析,语法分析可采用context.RegisterSourceOutput方法,该方法具有两个参数

  1. 声明一个分部类和分布方法
namespace SourceGenerator2
{public static partial class GreetingUsePartialClass{public static partial void SayHelloTo2(string name);}
}
  1. 新建一个类库,也就是语法生成器项目
[Generator(LanguageNames.CSharp)]
public sealed class GreetingIncrementalGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){context.RegisterSourceOutput(//CreateSyntaxProvider接受一个判断方法,判断是否满足要求,并返回一个语法树context.SyntaxProvider.CreateSyntaxProvider(NodePredicate,(gsc, _) => (MethodDeclarationSyntax)gsc.Node), //第二个参数为上一步返回的经过判断的语法树(spc, method) => {var type = method.Ancestors().OfType<TypeDeclarationSyntax>().First();var typeName = type.Identifier.ValueText;spc.AddSource($"{typeName}.g.cs",$$"""//加上这句话,告知编译器,这个文件是由源代码生成器生成的,//防止编译器进行代码分析,避免不必要的编译器警告//<auto-generated>#nullable enablenamespace SourceGenerator2;partial class {{typeName}}{public static partial void SayHelloTo2(string name){global::System.Console.WriteLine($"Hello, World {name}!");}}""");});}//判断分部方法是否满足要求private static bool NodePredicate(SyntaxNode node, CancellationToken _)=> node is MethodDeclarationSyntax{Identifier.ValueText: "SayHelloTo2",Modifiers: var methodModifiers and not [],ReturnType: PredefinedTypeSyntax{Keyword.RawKind: (int)SyntaxKind.VoidKeyword},TypeParameterList: null,ParameterList.Parameters:[{Type: PredefinedTypeSyntax{Keyword.RawKind: (int)SyntaxKind.StringKeyword}}],Parent: ClassDeclarationSyntax{Modifiers: var typeModifiers and not []}}&& methodModifiers.Any(SyntaxKind.PartialKeyword)&& typeModifiers.Any(SyntaxKind.PartialKeyword)&& methodModifiers.Any(SyntaxKind.StaticKeyword);
  1. 使用,在主项目中输入

GreetingUsePartialClass.SayHelloTo2("SourceGenerator2");

image-20231115143518356

3. 使用ForAttributeMetadataName

类似于C#源代码生成器深入讲解一中05章节所讲,如果想要借助特性来使用代码生成器,则可以使用ForAttributeMetadataName方法

  1. 在主项目中声明特性
namespace SourceGenerator2
{[AttributeUsage(AttributeTargets.Method)]public sealed class SayHello2Attribute:Attribute;
}
  1. 在主项目中声明一个分部方法,并标记特性
namespace SourceGenerator2
{public static partial class GreetingUsePartialClass{[SayHello2]public static partial void SayHelloToAttribute(string name);}
}
  1. 建立代码生成器项目
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Xml.Linq;namespace SourceGenerator2.UseAttribute
{[Generator(LanguageNames.CSharp)]public class SourceGenerator2UseAttribute : IIncrementalGenerator{public void Initialize(IncrementalGeneratorInitializationContext context){#regioncontext.RegisterSourceOutput(//与上一章节中的区别是使用了ForAttributeWithMetadataName来创建Provider//RegisterSourceOutput第一个参数,IncrementalValueProvider<TSource>context.SyntaxProvider.ForAttributeWithMetadataName("SourceGenerator2.SayHello2Attribute",NodePredicate,static (gasc, _) => gasc switch{{TargetNode: MethodDeclarationSyntax node,TargetSymbol: IMethodSymbol{Name: var methodName,TypeParameters: [],Parameters: [{ Type.SpecialType: SpecialType.System_String, Name: var parameterName }],ReturnsVoid: true,IsStatic: true,ContainingType:{Name: var typeName,ContainingNamespace: var @namespace,TypeKind: var typeKind and (TypeKind.Class or TypeKind.Struct or TypeKind.Interface)}}} => new GatheredData{MethodName = methodName,ParameterName = parameterName,TypeName = typeName,Namespace = @namespace,TypeKind = typeKind,Node = node},_ => null}//Collect,combine等方法可对Provider进行组合).Collect(),//RegisterSourceOutput第二个参数,Action<SourceProductionContext, TSource>(spc, data) =>{foreach (var item in data){if (item is null){continue;}var namespaceName = item.Namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);namespaceName = namespaceName["global::".Length..];var typeKindString = item.TypeKind switch{ TypeKind.Class => "class", TypeKind.Struct => "struct", TypeKind.Interface => "interface", _ => throw new NotImplementedException() };spc.AddSource($"{item.TypeName}.g.cs",$$"""// <auto-generated/>#nullable enablenamespace {{namespaceName}};partial {{typeKindString}} {{item.TypeName}}{{{item.Node.Modifiers}} void {{item.MethodName}}(string {{item.ParameterName}})=> global::System.Console.WriteLine($"Hello, {{{item.ParameterName}}}!");}""");}});#endregion}public bool NodePredicate(SyntaxNode node, CancellationToken token) => node is MethodDeclarationSyntax{Modifiers: var modifiers and not [],Parent: TypeDeclarationSyntax { Modifiers: var typemodifiers and not [] }}&& modifiers.Any(SyntaxKind.PartialKeyword)&& typemodifiers.Any(SyntaxKind.PartialKeyword);}
}file class GatheredData
{public string MethodName { set; get; }public string ParameterName { set; get; }public string TypeName { set; get; }public INamespaceSymbol Namespace { set; get; }public TypeKind TypeKind { set; get; }public MethodDeclarationSyntax Node { set; get; }
}

4. CompilationProvider和AdditionalTextsProvider

很多源代码生成器都是针对程序集的,而不是针对某个类的,所以使用CompilationProvider

[Generator(LanguageNames.CSharp)]
public class MyTupleGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){//很多源代码生成器都是针对程序集的,而不是针对某个类的,所以使用CompilationProvider//因为CompilationProvider提供的功能有限,本次要使用TextsProvider,所以要使用Combine方法进行组合context.RegisterSourceOutput(context.CompilationProvider.Combine(context.AdditionalTextsProvider.Collect()), Output);}private void Output(SourceProductionContext spc, (Compilation, ImmutableArray<AdditionalText>) pair){var (compilation, additionalFiles) = pair;spc.AddSource("mytuble.g.cs","source...省略");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/154503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

维基百科文章爬虫和聚类【二】:KMeans

维基百科是丰富的信息和知识来源。它可以方便地构建为带有类别和其他文章链接的文章&#xff0c;还形成了相关文档的网络。我的 NLP 项目下载、处理和应用维基百科文章上的机器学习算法。 一、说明 在我的上一篇文章中&#xff0c;展示了该项目的轮廓&#xff0c;并奠定了其基础…

C#中的is和as的使用和区别

目录 概述一、is操作符1. is操作符的语法2. is操作符的用途3. is操作符的使用示例4. is操作符与typeof操作符的区别 二、as操作符1. as操作符的语法2. as操作符的用途3. as操作符的使用示例4. as操作符与is操作符的区别和联系5. as操作符与is操作符的区别总结 概述 在C#编程语…

深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序 计算机竞赛

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习&#xff1f;5.1.2 为什么要迁移学习&#xff1f; 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

c++语言核心及进阶

核心编程 内存分区模型 根据c执行将内存划分为5个区域&#xff1a; 代码区&#xff0c;存放函数体的二进制&#xff0c;即CPU执行的机器指令&#xff0c;并且是只读的&#xff1b;常量区&#xff0c;存放常量&#xff0c;即程序运行期间不能被改变的量。全局区&#xff08;静…

练习八-利用有限状态机进行时序逻辑的设计

利用有限状态机进行时序逻辑的设计 1&#xff0c;任务目的&#xff1a;2&#xff0c;RTL代码&#xff0c;及原理框图3&#xff0c;测试代码&#xff0c;输出波形 1&#xff0c;任务目的&#xff1a; &#xff08;1&#xff09;掌握利用有限状态机实现一般时序逻辑分析的方法&am…

淘宝商品详情接口,商品属性接口,商品信息查询,商品详细信息接口,h5详情,淘宝APP详情

淘宝商品详情API接口可以使用淘宝开放平台提供的SDK或API来获取。这些接口可以用于获取商品的详细信息&#xff0c;如标题、价格、描述、图片等。 以下是使用淘宝开放平台API获取商品详情的步骤&#xff1a; 注册淘宝开放平台账号&#xff0c;并创建应用&#xff0c;获取应用…

重装系统后如何恢复以前的文件?详细教程大揭秘!

在日常生活中&#xff0c;我们可能会遇到各种计算机问题&#xff0c;其中最严重的问题之一就是需要重装系统。在重装系统之前&#xff0c;我们通常需要考虑一个问题&#xff1a;重装系统后还能恢复以前的文件吗&#xff1f; 首先&#xff0c;我们需要明确一点&#xff0c;重装…

7-tcp 三次握手和四次挥手、osi七层协议,哪七层,每层有哪些?tcp和udp的区别?udp用在哪里了?

1 tcp 三次握手和四次挥手 2 osi七层协议&#xff0c;哪七层&#xff0c;每层有哪些 3 tcp和udp的区别&#xff1f;udp用在哪里了&#xff1f; 1 tcp 三次握手和四次挥手 # tcp协议---》处于osi7层协议的传输层&#xff0c;可靠连接&#xff0c;使用三次握手&#xff0c;四次挥…

PLC通过Modbus转profinet网关读取并控制恒压供水系统中的变频器频率

PLC通过Modbus转profinet网关读取并控制恒压供水系统中的变频器频率 PLC通过Modbus转Profinet网关(XD-MDPN100)在恒压供水系统中读取变频器的频率。该系统实时监控逆变器的频率&#xff0c;以确保水的供应能够保持恒定的压力。PLC通过Modbus与变频器通信&#xff0c;将读取的频…

Redis高可用之持久化

Redis的高可用 在集群当中有一个非常重要的指标&#xff0c;提供正常服务的时间的百分比(365),99.9%后面的小数点越多说明越可靠。Redis 的高可用含义更加宽泛&#xff0c;正常服务是指标之一&#xff0c;数据容量的扩展&#xff0c;数据的安全性。 redis中高可用技术种类 1…

Altium Designer学习笔记3

原理图生成PCB&#xff1a; 然后是手动布局&#xff1a; 可以看到先没有交叉。 最终再走线。 另外&#xff0c;了解下这个封装的一些概念。

线程的面试八股

Callable接口 Callable是一个interface,相当于给线程封装了一个返回值,方便程序猿借助多线程的方式计算结果. 代码示例: 使用 Callable 版本,创建线程计算 1 2 3 ... 1000, 1. 创建一个匿名内部类, 实现 Callable 接口. Callable 带有泛型参数. 泛型参数表示返回值的类型…

110. 平衡二叉树

110. 平衡二叉树 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;递归法&#xff1a;迭代法 错误经验吸取 原题链接&#xff1a; 110. 平衡二叉树 https://leetcode.cn/problems/balanced-binary-tree/description/ 完成情况&#xff1…

智慧工地网络广播系统

智慧工地网络广播系统 智慧工地网络广播&#xff0c;是智慧公司不可缺少的一环&#xff0c;对于工地广播来说&#xff0c;音质和传输稳定性都是非常重要的要素。尤其是在高楼大厦密集的地方&#xff0c;可能会存在信号干扰和传输受阻的情况&#xff0c;这时候可以考虑使用网络…

VueH5公众号分享到微信朋友圈或好友

场景需求&#xff1a; 一般分享场景是在当前页面分享当前页面&#xff0c;但是业务需求是&#xff0c;在当前页面分享好几个其他页面的链接到朋友圈和好友。 PS&#xff1a;微信自带的分享面板是无法第三方唤起的&#xff0c;只能点三个点。 其次在微信公众号页也不支持自定义…

redis---非关系型数据库

关系数据库与非关系型数据库 redis非关系型数据库&#xff0c;又名缓存型数据库。数据库类型&#xff1a;关系型数据库和非关系型数据库关系型数据库是一 个机构化的数据库,行和列。 列&#xff1a;声明对象。 行&#xff1a;记录对象属性。 表与表之间的的关联。 sql语句&…

Linux fork和vfork函数用法

fork和vfork是用于创建新进程的函数&#xff0c;在Linux的C语言编程中非常常见。 fork函数 fork函数是用于创建一个新的进程&#xff0c;新进程是调用进程的副本。新进程将包含调用进程的地址空间、文件描述符、栈和数据。在fork之后&#xff0c;父进程和子进程将并发执行。 …

【giszz笔记】产品设计标准流程【5】

&#xff08;续上回&#xff09; 目录 五、原型设计 1.写在前面的话 2.原型是什么 3.画原型的工具 4.产品经理的复合能力 5.关于原型图 PS&#xff1a;这个系列&#xff0c;主要讨论的是产品设计的一般标准流程。这个流程也许每天都发生在我们的身边&#xff0c;我们也常…

MatrixOne完成与麒麟信安、欧拉的兼容互认

近日&#xff0c;超融合异构云原生数据库MatrixOne企业版软件V1.0完成了与欧拉开源操作系统&#xff08;openEuler简称“欧拉”&#xff09;、麒麟信安操作系统系列产品和虚拟化平台的相互兼容认证&#xff0c;通过了欧拉兼容性测评&#xff0c;获得了《openEuler技术测评证书》…

Pycharm run 输出界面控制一行能够输出的元素个数

Pycharm run 输出界面控制一行能够输出的元素个数 今天遇到了一个问题&#xff0c;当我们在 Pycharm 中打印输出数组时&#xff0c;如果数组一行的元素个数过多&#xff0c;那么我们在打印时就会出现以下问题。 代码如下&#xff1a; import numpy as npx np.array([[0., 0.7…