浅谈源代码生成技术(Source Generators)

即便 .NET5已经发布了很多年,但许多人依然对这一项令人眼前一亮的新技术 - Source Generators (源代码生成)不怎么熟悉, 如果您有兴趣,可以花5分钟看我白话白话。

8ac5e755576c51b6765a9a745cf112f3.jpeg

起初看到这项技术时甚至引发了我颅内 “高潮” :心说再也不用纠结代理类的种种限制了;通过翻译我们写代码更方便了;进一步提升开源库们的性能;削弱了注解入侵的影响,甚至注释也利用起来了;模板定制?叫T10086吧。

怀揣着对技术大饼的幻想,懒癌晚期的我终于在爬上了椅子下载了 VS 预览版,迫不及待的打开 Demo,正准备尝鲜的时候,突然 VS 编译报错,??,!!,??崩了,奋战1小时后我放弃了,脑潮褪去,再次提起 SG(懒的写 Source Generators )的时候是在一篇 Natasha 文章中:技术很抢眼,应用不值得。

61fba795ec47e3e686f7b36277873a64.png

日子混了挺久,直到群里老九说 SG 可用了,其他开源库的作者们三言两语带来了一波新的展望,我便寻思了两晚决定重新试一试。挑了个良辰吉日,卸载预览版,把 VS 升级到最新版,下载 Demo,跑起来了,VS 无异常,控制台秒开,Hello World! 字体清晰可见,我猜这次八成是没啥事了。通过群内讨论与调研,为了不欠技术债,这里我把 SG 技术整体介绍一下:

c5ada94128c385e9e5534fd29fde9453.jpeg

引用官方的一段话做个技术上的解释:

Source Generator 是 C#开发人员可以编写的一种新型组件,它使您可以做两件事:检索一个 Compilation 对象,该对象表示所有正在编译的用户代码。可以检查此对象,并且您可以编写与正在编译的代码的语法和语义模型一起使用的代码,就像今天的分析器一样。生成可在编译过程中添加到 Compilation 对象的C#源文件。换句话说,您可以在编译代码时提供其他源代码作为编译的输入。源生成器是 .NET Standard 2.0 程序集,由编译器与任何分析器一起加载。它可以在可以加载和运行 .NET Standard 组件的环境中使用。

这里先排除个范围:

1、代码优化

2、日志注入

3、IL 织入

4、调用位重写

以上是 SG 明确不支持的功能,同时,源代码生成器不是分析器,各位不要混淆了。

SG 主要工作单元是 ISourceGenerator 接口,其 Execute 方法参数是携带有 Compilation 对象的上下文,Compilation 是什么呢,如果写过动态编译的同学可能知道,这是在 Emit 发出前构造出的编译信息集合,其中囊括了编译选项,语法树集合,各种编译标识等,Compilation 作为各语言编译信息的抽象结构,是有能力支持 VB / F# 等语言平台的,另外 Compilation 的语法树集合如果允许被替换,那么也可以做很多侵入式操作,  比如内个AOP,  千万别高兴太早,往下看。 

默认情况下,实现 ISourceGenerator  的生成器看不到 IDE 的感知,只有在生成的时候才会把信息搜集全,现有版本的 SG 功能有限,想改已写好的代码不可能,最多是结合部分类增加功能,目前所有的操作都是加法, 所以上面说的 AOP  还是得代理。

1b5c5307aa77aedca999a940d20a29f0.jpeg

我们看看官方有什么动态,官方跟一批狠人(grpc / Razor / Blazor等甲方)讨论了新的需求,新版中将使用 初始化上下文 GeneratorInitializationContext  对文件进行监控,还将结合 MSBuild 相关属性完善文件输出等功能。有个挺恶心的问题,文件输出之后调试如何打断点,断点如何起作用,还待解决。这里我多嘴一句,有些新功能要设计成什么样,官方也没定下来。

那么目前来看,官方将从中获取哪些收益呢:

  • ASP.NET: 减少了启动时间。

  • Blazor and Razor:  大幅降低了工作负担

  • 序列化 / GRPC / Azure / SWIG 都有用到

为了快一点适应这项技术以便定了个小目标,简单走一遍静态AOP的实现。接下来陈述的问题也许就是目前有些人遇到的问题(以下内容是由群友和开源库作者们及个人挖掘的提问与讨论组成的):

4aa46ebd96c97eeddbd3e5570e89eeef.jpeg

1、就在我写到上面问题冒号的时候,群友问了个问题,为啥我项目跑不起来,没有提示?

经过查看他的截图我确定他应该是不太清楚这个引用结构。准备:

控制台 A ,类库 B;   

A 写测试代码, B写代码分析和生成;

A 引用 B;B 引用 Roslyn 相关的库;

注意看A引用节点的属性

<ItemGroup>
        <ProjectReference Include="B.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

2、如何拿到我定义的类?

首先要实现 ISourceGenerator 接口

1、通过 SourceGeneratorContext 的 AdditionalFiles 读取.cs 文件。

2、通过 SourceGeneratorContext 的 Compilation 获取语法树集合。

3、官方建议给你的类做一些标记,然后通过注册  SyntaxReceiver 实现在树上游走查找标记,来实现记录与过滤,详见属性通知的那个例子。

注:后续的新版本 SourceGeneratorContext  将改名为 GeneratorExecutionContext

这里我表述一下官方推荐的流程,刚接触的人如果不清楚这里肯定会发懵:

    1) 实现 ISyntaxReceiver 的 OnVisitSyntaxNode 方法。

    2)   写你的 demo , 按 F5 / F6 生成;

   3)   你写的代码瞬间被翻译成语法树,编译器马上遍历语法树,走一个节点进一次 OnVisitSyntaxNode 方法,满足你口味的节点就记录下来。

    4) Recevier 携带选中的节点,被装进上下文 SourceGeneratorContext 中并进入到 ISourceGenerator 接口的 Execute 方法,此时你的 Recevier 中的信息是所有符合条件的信息,就算你获取了类节点,也是一堆类节点,这里需要手动处理和区分一下。

笔者推荐的流程:

不要把你想改造的类写到 .cs 文件中,因为当前的源代码生成技术不允许你改造现有的类,不允许也不支持,没有语法树替换这一说。允许你添加源代码,允许你操作部分类,就是不允许你改造这个已经存在的 cs 文件。

我个人建议定制模板,让 SG 项目解析模板再添加到你的项目中,也不耽误使用,也不耽误引用,但是对于生态来说,肯定要有统一的规范约束才行。

c2b0a9f6942aeb7e260d6c32bf306bb9.png

通过SG生成之后:

public UseMethodModel Show3(ref int a, out string b)
{
       UseMethodModelAopProxy.BeforeShow3?.Invoke(this,ref a,out b);


       Console.WriteLine("In Show3()");
       b = "b";
       
       UseMethodModelAopProxy.AfterShow3?.Invoke(this,ref a,out b);
       return this;


}

如下是 AOP 的使用代码:

当前 例子结合了 Natasha 可以做到运行时动态脚本赋值。

f0c08c8886cf8345f6ac424e08fbd29d.png


运行结果

77061d16de83c7dd007c75203e30834c.png

3、改了不生效啊?

VS 日常玄学操作,右键清理,两个项目都清理。再跑就好了。

4、还是不行啊,爆红。

发生在编译期,要不要编译一下试试?跑通了关掉 VS , 再打开就全绿了。

跑了,还是不行啊?

可能是 SG 项目引发异常了,自己排查吧。

5、不会语法树操作。

可以参考这个项目,有人参与维护的话会发布:https://github.com/dotnet-lab/Papper ,没有就本地引用吧。

6、打包使用?

<ItemGroup>
    <!-- Package the generator in the analyzer directory of the nuget package -->
    <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>

6e3b15aa25050e46da15324055c41d87.png

用 GeneratePathProperty = true 生成对应包的 Pkg 变量,然后输出。

b5b0c84f0839bf44f6687e8cd6db8166.png

模板以及注释我还没有写Demo, 原谅我没有太多的精力和时间去投入到这件事中,但经过 AOP 项目我可以确信这两个功能可以实现。以上问题已经囊括了大部分尝鲜的坑,各位可以试一试,客观的说,目前 API 还在变,VS 在实验中出现各种灵异事件(目前我的 VS 出现了后遗症)并且官方对这项技术对生态的影响还保持一个观望的态度,所以建议各位稍安勿躁,可以定制计划,实验调研,但上生产还是要谨慎一些!!最后记得点赞,并看一看。

eeaa65b8f20a954fe70e7ac8ba5c3dfa.png

    啥也不是,散会!


https://github.com/dotnetcoreb4b71abcb25d09653c4c63c51afe71af.gif

打赏一杯酒,削减三分愁。
跟着我们走,脱发包你有。

996cd5645046b77cbbb882bba13cb07e.png

87caddae29dffac6dfce3327ca7e914f.jpeg

组织打赏账户为柠檬的账户,请标注「NCC」,并留下您的名字,以下地址可查看收支明细:https://github.com/dotnetcore/Home/blob/master/Statement-of-Income-and-Expense.md55527a3215545c9f35592a96db02673a.pngd35de371f68910ba56890d5e00dafc64.png

OpenNCC,专注.NET技术的公众号

https://www.dotnetcore.xyz

0c4229d051d6a83a859a3ccd853d8881.png

9fb4cf5b7607b831f09e32070095672c.png微信ID:OpenNCC

fd730f8e4f463f9bc27f0865d3873bdb.png长按左侧二维码关注

欢迎打赏组织

给予我们更多的支持

93879b248f392c2bd4ab1d15ce100fae.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Parcelable Code Generators是一种用于自动生成Android Parcelable接口相关代码的工具。在Android开发中,Parcelable接口常常用于在Activity之间传递自定义对象。但是手动实现Parcelable接口并编写相关的代码是繁琐且容易出错的,因此开发人员常常使用Parcelable Code Generators来自动生成这些代码。 Parcelable Code Generators可以帮助开发人员简化Parcelable接口的实现过程。它通过解析自定义对象的属性和方法,生成相应的Parcelable接口实现。开发人员只需要在自定义对象上添加一些注解或配置,然后使用Parcelable Code Generators生成器工具即可自动生成Parcelable相关的代码,包括序列化和反序列化的方法。 使用Parcelable Code Generators的好处是提高了开发效率,减少了手动实现Parcelable接口所需的工作量。开发人员只需要关注对象的定义和注解的配置,而无需费心编写复杂的序列化和反序列化代码。此外,Parcelable Code Generators还可以保证生成的代码的正确性和稳定性,避免了手动编写代码过程中可能出现的错误。 总之,Android Parcelable Code Generators是一种实用工具,能够帮助开发人员自动生成Parcelable相关代码,提高开发效率,减少错误。使用它可以简化开发过程,使得在Activity之间传递自定义对象变得更加方便和可靠。 ### 回答2: Android中实现Parcelable接口可以用于实现对象的序列化和反序列化,方便数据的传递和保存。然而,手动编写Parcelable的代码需要写入大量重复的代码,比较繁琐且易出错。因此,有一些代码生成器可以用来自动生成Parcelable代码,简化并加速开发过程。 一种常用的Android Parcelable代码生成器是插件"Parcelable Code Generator"。这个插件可以作为Android Studio的一个扩展来安装和使用。Parcelable Code Generator使用注解处理器,可以根据模板自动生成Parcelable相关代码。 使用这个插件,我们只需要在需要实现Parcelable的Java类上添加一个注解,例如"@Parcelable"。然后,编译项目时插件会自动扫描注解,根据类的字段自动生成Parcelable的代码。生成的代码中包含了Parcelable接口的实现和parcelable的函数,这样就可以将对象序列化到Parcel中,或者将Parcel中的数据还原到对象中。 Parcelable Code Generator还支持一些配置选项,比如可以指定Parcelable的名称、包名、是否实现Parcelable.Creator等。此外,插件还支持自定义Parcelable代码的模板,可以根据需求自定义生成的Parcelable代码的格式和内容。 总之,Android Parcelable Code Generator插件简化了Parcelable代码的编写,提高了开发效率和代码质量。使用这个插件,可以省去手动写入Parcelable的代码,减少了出错的可能性,节省了开发时间。它是Android开发中的一种常见工具,为我们提供了方便快捷的Parcelable代码生成功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值