C#源代码生成器深入讲解上
01 源代码生成器初体验
- 新建一个类库,一定是standard2.0版本,否则会出问题。
- 引用Nuget包
Microsoft.CodeAnalysis.Common
- 新建一个类,继承自
ISourceGenerator
接口
1 | //一定要写,制定语言 |
注意事项
- 在使用某些方法或者特性时,最好写全命名空间,并用
global::
命名空间限定符 - 文件名称建议使用
.g.cs
后缀 - 建议在开头增加
<auto-generated>
注释 - 可以使用原生字符串符号
"""
三个双引号,以及双内插符号$$
,这样可以使用{{}}
来进行内插
建立一个控制台项目,并引用刚才的类库,要加上
OutputItemType和 ReferenceOutAssembly
1
2
3
4<ItemGroup>
<!--ReferenceOutAssembly设定false,表示不会将生成器的作为引用,而是将分析器生成的代码。-->
<ProjectReference Include="..\SourceGeneratorConsole.Generator\SourceGeneratorConsole.Generator.csproj" OutputItemType="Analyzer" ReferenceOutAssembly="false" />
</ItemGroup>使用源代码生成器,使用生成器中所生成的方法。
1 | using GreetingTest; |
02 使用分部类型
很多时候,不需要源代码生成器生成完整的类型,而是和主程序交互,分别形成一定的代码,此时可以使用分部类型来实现。
- 在上一章节中的控制台项目中增加一个类
1 | namespace GreetingTest |
- 修改上一章节中源代码生成器类库项目
1 | namespace SourceGeneratorConsole.Generator; |
- 在控制台应用中调用
1 | static void Main(string[] args) |
03 使用SyntaxReceiver属性
上一章节中,在源代码生成器中将类名和方法名写进去了,源代码生成器往往是应用在不同的项目中,类型名和方法名都不是固定的,所以要动态的修改名称,这就要用到了SyntaxContextReceiver属性。
- 在上一章节中的源代码生成器文件中,写一个
SyntaxReceiver
类
1 | //file只在本文件可以用,跟internal一样是访问修饰符 |
- 修改属性生成器
1 | [ ] |
在Initialize中返回刚才创建的类, Execute方法中获得相应的类名称。
- 调用
1 | static void Main(string[] args) |
04 调试源代码生成器
源代码生成器是在编译阶段中自动生成,一般无法调试,这时可以在源代码生成器中的Initialize
方法中加上
1 | //添加调试器,如果程序没有调试器的时候就启动 |
05 ISyntaxContextReceiver属性
上面是已知有了SayHello的方法,假设不知道是什么方法名,如何使用源代码生成器,本节借助特性来实现
- 在主项目中声明特性,一般都是放在主项目中,因为在主项目中的引用其他项目的设置中已设置了
OutputItemType="Analyzer" ReferenceOutAssembly="false"
,这表示不会将生成器的作为引用,而是将分析器生成的代码,如果将特性定义在生成器中,主项目引用不到特性定义
1 | namespace SourceGeneratorConsole |
- 在主项目中声明一个分部方法
1 | namespace SourceGeneratorConsole |
- 按照上面的流程创建源代码生成器
1 | namespace SourceGeneratorConsole.UseAttributes |
- 使用源代码生成器
1 | GreetingUseAttribute.SayHi("使用特性的属性生成器"); |
06 自定义MyTuble类型实战
我们经常用到Func
泛型委托,该泛型委托最多支持16个参数和一个返回值,因为泛型定义没有类似于可变参数的功能,对于不同数量的泛型参数一定要定义同数量的泛型定义。类似于下面这样。
1 | Func<TResult> |
我们仿照Func泛型委托自定义一个MyTuple泛型类型
- 先定义一个MyTuple模板,这是一个定义了2个泛型参数的MyTuple类型,根据该模板要定义支持多个泛型参数的MyTuple类型
1 | public readonly struct MyTuple<T1, T2>(T1 value1, T2 value2) : |
- 写一个源代码生成器,根据上面的模板进行改造,自动生成含有1-8个泛型参数的MyTuple类型,其根本原理就是字符串的操作。
1 | [ ] |
- 主项目引用源代码生成器后,使用MyTuple
1 | var myTuple1 = new MyTuple<int, double>(1, 3.0); |
07AdditionalFiles的使用
上一章节中,我们在直接定义了MyTuple时设置最大泛型参数数量为8,如果我们需要根据需要来设置最大泛型参数数量,则可以在主项目中增加一个配置文件,文件中对此进行设置,并在源代码生成器中使用GeneratorExecutionContext的AdditionalFiles
属性来处理非代码文件
。
- 在主项目中增加一个文件,本次案例增加一个
MyTupleMaxTypeArgumentCount.txt
文件,在该文件中写入4。 - 在主项目配置中,增加
1 | <ItemGroup> |
- 在06章节中源代码基础上,增加读取本地文件功能
1 | [ ] |
08自定义编译器诊断信息
在进行编译时,编译器会自动给出编译信息供用户查看,通常编译器诊断信息如下所示。
由于源代码生成器会自动后台生成,所以给出诊断信息是十分必要的。本章节根据07章节中的章节,给出自定义编译器诊断信息的
1 | [ ] |
随便改一下MyTupleMaxTypeArgumentCount.txt里面的内容为非数字类型,则会收到
C#源代码生成器深入讲解上