学习Source Generators之IIncrementalGenerator
前面我们用ISourceGenerator来实现代码生成。但是在官方的介绍中有这么一个警告:Warning: Source generators implementing ISourceGenerator have been deprecated in favor of incremental generators.
意思是实现ISourceGenerator的源生成器已被弃用,取而代之的是增量生成器。
那么本文将会介绍IIncrementalGenerator来实现我们的代码生成。后续的文章我们都只会使用IIncrementalGenerator来介绍和学习。
介绍
在官方的介绍中是这样介绍的:增量生成器是一种新的 API,与源生成器一起存在 ,允许用户指定可由托管层以高性能方式应用的生成策略。
High Level Design Goals
- 允许使用更细粒度的方法来定义生成器
- 缩放源生成器以支持 Visual Studio 中的“Roslyn/CoreCLR”缩放项目
- 利用细粒度步骤之间的缓存来减少重复工作
- 支持生成更多项目而不仅仅是源文本
- ISourceGenerator与基础实现并存
使用IIncrementalGenerator实现HelloWorld
我们将HelloSourceGenerator的继承接口修改成IIncrementalGenerator并实现Initialize方法。
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace HelloWorld_IncrementalGenerator.Analysis
{
[Generator]
public class HelloSourceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var compilation = context.CompilationProvider;
context.RegisterSourceOutput(compilation, (sourceProductionContext, c) =>
{
var mainMethod = c.GetEntryPoint(sourceProductionContext.CancellationToken);
string source = $@"// <auto-generated/>
using System;
namespace {mainMethod.ContainingNamespace.ToDisplayString()}
{{
public static partial class {mainMethod.ContainingType.Name}
{{
static partial void Hello(string name) =>
Console.WriteLine($""Hello: '{{name}}'"");
}}
}}
";
var typeName = mainMethod.ContainingType.Name;
var sourceText = SourceText.From(source, Encoding.UTF8);
sourceProductionContext.AddSource($"{typeName}.g.cs", sourceText);
});
}
}
}
其他地方完全无需改动。启动编译。可以看到成功生成代码。
启动项目:
可以看到效果是一模一样的。
基于管道的执行
增量生成器不是使用用Execute方法,而是定义不可变的执行管道作为初始化的一部分。该 Initialize方法接收一个实例, 生成器使用IncrementalGeneratorInitializationContext实例来定义一组转换。
输入数据以不透明数据源的形式提供给管道,可以是IncrementalValueProvider
上面我我们使用的CompilationProvider类型就是就是IncrementalValueProvider
目前官方提供可用的Providers有如下几种:
- CompilationProvider
- AdditionalTextsProvider
- AnalyzerConfigOptionsProvider
- MetadataReferencesProvider
- ParseOptionsProvider
比如我们需要读取文件信息,可以使用AdditionalTextsProvider。
我们可以使用Select对Provider进行数据的转换。这个方法跟Linq有点像,但是不是Linq。
下面是官方的一个示例,表示对数据的一个转换处理。transformed和prefixTransform也都是IncrementalValuesProvider实例,表示转换的结果,而不是数据本身。
// get the additional text provider
IncrementalValuesProvider<AdditionalText> additionalTexts = initContext.AdditionalTextsProvider;
// apply a 1-to-1 transform on each text, which represents extracting the path
IncrementalValuesProvider<string> transformed = additionalTexts.Select(static (text, _) => text.Path);
// transform each extracted path into something else
IncrementalValuesProvider<string> prefixTransform = transformed.Select(static (path, _) => "prefix_" + path);
注意:在使用AdditionalTextsProvider的时候。需要在项目文件中添加AdditionalFiles。
比如:
<ItemGroup>
<AdditionalFiles Include="*.json" />
</ItemGroup>
否则生成代码的操作不会被触发。
结语
本文章简单介绍了使用IIncrementalGenerator来进行代码生成的方式以及他的执行逻辑。下一篇我们将详细介绍IncrementalValueProvider以及使用。
本文代码仓库地址https://github.com/fanslead/Learn-SourceGenerator