IIncrementalGenerator 如何在源代码生成器单元测试提供 AnalyzerConfigOptionsProvider 选项

news/2025/11/27 7:31:49/文章来源:https://www.cnblogs.com/lindexi/p/19275194

本文是 为 IIncrementalGenerator 增量 Source Generator 源代码生成项目添加单元测试 的后续。在上文介绍了如何给增量 Source Generator 源代码生成项目添加单元测试,本文将在此基础上,告诉大家如何提供 AnalyzerConfigOptionsProvider 选项

先来看看一个简单的源代码生成器的例子,以下的代码将根据配置的 FooProperty 属性决定生成的代码内容

[Generator(LanguageNames.CSharp)]
public class IncrementalGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){IncrementalValueProvider<string> configurationProvider = context.AnalyzerConfigOptionsProvider.Select((t, _) =>{var globalOptions = t.GlobalOptions;if (globalOptions.TryGetValue("build_property.FooProperty", out var property)){return property;}return null;});context.RegisterSourceOutput(configurationProvider, (productionContext, configurationProperty) =>{productionContext.AddSource("GeneratedCode.cs",$$"""using System;namespace LurlelnarkallChijurjeaqelba{public static class GeneratedCode{public static void Print(){Console.WriteLine("配置的属性 {{configurationProperty}}");}}}""");});}
}

注: 为了让我的博客引擎开森,以上代码部分花括号被我替换为了全角花括号。大家在使用的时候需要将全角花括号替换为半角花括号

以上的 build_property.FooProperty 就是获取某个属性配置的写法,正常来说是需要依靠 CompilerVisibleProperty 指定具体的属性名才能被源代码生成器访问到的,详细请参阅 IIncrementalGenerator 增量 Source Generator 生成代码入门 读取 csproj 项目文件的属性配置

在正式项目里面大概的写法如下:

  <PropertyGroup><FooProperty>lindexi is doubi</FooProperty></PropertyGroup><ItemGroup><CompilerVisibleProperty Include="FooProperty" /></ItemGroup>

以上代码既可以写到 csproj 项目文件里面,也可以被放在带在 NuGet 包里的 props 文件里面。如果对此机制感觉到陌生,还请参阅 dotnet 源代码生成器分析器入门

单元测试里面,需要使用 CSharpGeneratorDriver.Create 的重载方法传入 AnalyzerConfigOptionsProvider 类型参数

由于 AnalyzerConfigOptionsProvider 是抽象的,我添加了如下代码用于辅助测试

internal class TestAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
{public TestAnalyzerConfigOptionsProvider(Dictionary<string, string> configOptions){var testAnalyzerConfigOptions = new TestAnalyzerConfigOptions(configOptions);GlobalOptions = testAnalyzerConfigOptions;}public override AnalyzerConfigOptions GetOptions(SyntaxTree tree){return GlobalOptions;}public override AnalyzerConfigOptions GetOptions(AdditionalText textFile){return GlobalOptions;}public override AnalyzerConfigOptions GlobalOptions { get; }
}internal class TestAnalyzerConfigOptions : AnalyzerConfigOptions
{public TestAnalyzerConfigOptions(Dictionary<string, string> configOptions){_configOptions = configOptions;}private readonly Dictionary<string, string> _configOptions;public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value){return _configOptions.TryGetValue(key, out value);}
}

通过 TestAnalyzerConfigOptionsProvider 辅助代码,可以使用字典表示将要注入到测试里面的属性,代码如下

        // 创建 AnalyzerConfigOptionsvar configOptions = new Dictionary<string, string>{["build_property.FooProperty"] = "Test",};var analyzerConfigOptionsProvider = new TestAnalyzerConfigOptionsProvider(configOptions);

configOptions 传入到 CSharpGeneratorDriver.Create 方法里面,代码如下

        var compilation = CreateCompilation(...);var generator = new IncrementalGenerator();GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], optionsProvider: analyzerConfigOptionsProvider);

这里需要让 IncrementalGenerator 通过 AsSourceGenerator 扩展方法转换为 ISourceGenerator 类型,才能传入此重载方法里面

完成以上步骤之后,就可以调用 GeneratorDriver.RunGenerators 开始执行源代码生成器

        driver = driver.RunGenerators(compilation);

以下是示例的单元测试


[TestClass]
public class IncrementalGeneratorTest
{[TestMethod]public void Test(){var testCode ="""using System;namespace LurlelnarkallChijurjeaqelba;""";var compilation = CreateCompilation(testCode);var generator = new IncrementalGenerator();// 创建 AnalyzerConfigOptionsvar configOptions = new Dictionary<string, string>{["build_property.FooProperty"] = "Test",};var analyzerConfigOptionsProvider = new TestAnalyzerConfigOptionsProvider(configOptions);GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], optionsProvider: analyzerConfigOptionsProvider);driver = driver.RunGenerators(compilation);var runResult = driver.GetRunResult();Assert.HasCount(1, runResult.GeneratedTrees);foreach (var generatedTree in runResult.GeneratedTrees){var generatedCode = generatedTree.ToString();Debug.WriteLine(generatedCode);if (generatedTree.FilePath.EndsWith("GeneratedCode.cs")){var expected ="""using System;namespace LurlelnarkallChijurjeaqelba{public static class GeneratedCode{public static void Print(){Console.WriteLine("配置的属性 Test");}}}""";// 防止拉取 git 时出现的 \r\n 不匹配问题。能够解决一些拉取 git 的奇怪的坑,也就是在我电脑上跑的好好的,但为什么在你电脑上就炸了expected = expected.Replace("\r\n", "\n");Assert.AreEqual(expected, generatedCode.Replace("\r\n", "\n"));}}}private static CSharpCompilation CreateCompilation(string source)=> CSharpCompilation.Create("compilation",new[] { CSharpSyntaxTree.ParseText(source, path: "Foo.cs") },new MetadataReference[]{// 如果缺少引用,那将会导致单元测试有些符号无法寻找正确,从而导致解析失败// 在这里添加你自己的依赖库的引用}// 加上整个 dotnet 的基础库.Concat(MetadataReferenceProvider.GetDotNetMetadataReferenceList()),new CSharpCompilationOptions(OutputKind.ConsoleApplication));
}internal class TestAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider
{public TestAnalyzerConfigOptionsProvider(Dictionary<string, string> configOptions){var testAnalyzerConfigOptions = new TestAnalyzerConfigOptions(configOptions);GlobalOptions = testAnalyzerConfigOptions;}public override AnalyzerConfigOptions GetOptions(SyntaxTree tree){return GlobalOptions;}public override AnalyzerConfigOptions GetOptions(AdditionalText textFile){return GlobalOptions;}public override AnalyzerConfigOptions GlobalOptions { get; }
}internal class TestAnalyzerConfigOptions : AnalyzerConfigOptions
{public TestAnalyzerConfigOptions(Dictionary<string, string> configOptions){_configOptions = configOptions;}private readonly Dictionary<string, string> _configOptions;public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value){return _configOptions.TryGetValue(key, out value);}
}internal static class MetadataReferenceProvider
{public static IReadOnlyList<MetadataReference> GetDotNetMetadataReferenceList(){if (_cacheList is not null){return _cacheList;}var metadataReferenceList = new List<MetadataReference>();var assembly = Assembly.Load("System.Runtime");foreach (var file in Directory.GetFiles(Path.GetDirectoryName(assembly.Location)!, "*.dll")){try{metadataReferenceList.Add(MetadataReference.CreateFromFile(file));}catch{// 忽略}}_cacheList = metadataReferenceList;return _cacheList;}private static IReadOnlyList<MetadataReference>? _cacheList;
}

本文的代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 28cf0fb972be64983e4e5eb91d9dd62930f2d49d

以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 28cf0fb972be64983e4e5eb91d9dd62930f2d49d

获取代码之后,进入 Roslyn/LurlelnarkallChijurjeaqelba 文件夹,即可获取到源代码

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

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

相关文章

降ai率免费工具推荐:提升内容原创性的实用选择

在内容创作领域,随着AI生成技术的普及,如何降低内容被识别为AI生成的概率(即“降AI率”)成为许多创作者关注的问题。降AI率免费工具通过文本优化、原创度提升等功能,帮助用户在保持内容质量的同时,增强内容的原创…

Ai元人文构想理论体系——初级阶段愿景

Ai元人文构想理论体系——初级阶段愿景AI元人文构想:智枢——价值冲突的调谐引擎 在AI迈向认知智能的时代,我们面临一个核心困境:大语言模型能生成海量内容,却在价值冲突面前显得无能为力。"AI元人文"构…

前端纯小白使用AI开发实战:一个免费的在线拼图工具Collaigo

在社交媒体时代,无论是个人分享生活点滴,还是品牌运营社交媒体账号,拼贴图都成为了内容创作的重要形式。然而,我在使用现有工具时遇到了不少痛点:* **功能限制**:很多工具只能做简单的网格拼图,缺乏创意空间 …

PeachPie 1.1.13 发布支持最新PHP 8.5.0

PeachPie 是一个有趣的技术项目,它架起了一座连接 PHP 生态与 .NET 世界的桥梁。PHP的最新版本是PHP 8.5.0,已于2025年11月20日正式发布。PeachPie 1.1.13 版本也同步发布。这是一个版本上的提升,包含了一些安全更新…

[1141] Swich Control Key and Command Key in Macbook

[1141] Swich Control Key and Command Key in MacbookShort answer: you can’t tell macOS “use Ctrl+C for copy instead of ⌘C” directly – but you can make your Ctrl key behave like Command, so pressing…

读社会工程:防范钓鱼欺诈(卷3)03保护课程

读社会工程:防范钓鱼欺诈(卷3)03保护课程1. 保护课程 1.1. 批判性思维1.1.1. 很多时候人们把批判性思维和逆反、缺乏信仰或者为了质疑而质疑联系在一起1.1.2. 告诫自己不要事事都信以为真1.1.3. 攻击者不希望你思考…

2025年下半年公交候车厅品牌推荐前十指南

摘要 2025年下半年,公交候车厅行业持续发展,环保、智能化和定制化成为主流趋势。本文基于市场调研和用户反馈,整理出前十推荐品牌榜单,排名不分先后,旨在为采购商提供参考。榜单中的公司均经过综合评估,重点推荐…

2025年下半年电子站牌品牌口碑推荐榜单

摘要 2025年下半年,电子站牌行业随着智慧城市建设的推进,呈现出快速增长的趋势,环保节能和智能化成为主流方向。本文提供一份电子站牌品牌推荐榜单,基于行业数据、用户口碑和实际案例整理,榜单排名不分先后,仅供…

2025年下半年公交候车厅品牌选购终极指南:十大优质供应商推荐

文章摘要 随着城市公共交通系统的持续升级,2025年下半年公交候车厅行业迎来新一轮发展机遇。本文基于行业调研数据,为您推荐十家值得关注的公交候车厅品牌供应商,排名不分先后,旨在为采购决策提供参考。特别说明:…

2025年下半年电子站牌品牌综合推荐指南:十大优质供应商盘点

文章摘要 2025年下半年电子站牌行业迎来智能化升级浪潮,随着智慧城市建设的深入推进,电子站牌作为城市公共交通信息化的重要组成部分,其技术含量和服务质量要求不断提高。本文基于市场调研和用户反馈,整理出十家在…

高性能AI股票预测分析报告 - 2025年11月27日 - 06:28:00

高性能AI股票预测分析报告 - 2025年11月27日body { font-family: "Microsoft YaHei", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: rgba(51, 51, 51, 1); max-wi…

高性能AI股票预测分析报告 - 2025年11月27日

高性能AI股票预测分析报告 - 2025年11月27日body { font-family: "Microsoft YaHei", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: rgba(51, 51, 51, 1); max-wi…

增强AI股票预测分析报告 - 2025年11月27日

增强AI股票预测分析报告 - 2025年11月27日body { font-family: "Microsoft YaHei", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: rgba(51, 51, 51, 1); max-widt…

AI股票预测分析报告 - 2025年11月27日

AI股票预测分析报告 - 2025年11月27日body { font-family: "Microsoft YaHei", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: rgba(51, 51, 51, 1); max-width: 1…

Bazzite:专为游戏打造的即用型操作系统

Bazzite是基于Fedora Atomic Desktop的即用型游戏操作系统,支持Steam Deck、台式机和HTPC,预装NVIDIA驱动和游戏工具,提供开箱即用的游戏体验。Bazzite:专为游戏打造的即用型操作系统 项目简介 Bazzite是一个基于F…

大盘风险控制策略分析报告 - 2025年11月27日

大盘风险控制策略分析报告 - 2025年11月27日body { font-family: "Microsoft YaHei", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: rgba(51, 51, 51, 1); max-wi…

使用spaCy构建可定制NLP管道

本文介绍了如何使用spaCy库构建可定制的自然语言处理管道,包括分词器定制、模型训练、配置文件和实体链接等核心技术。内容涵盖spaCy v3新特性、Thinc深度学习框架及实际应用案例。使用spaCy构建可定制NLP管道 NLP简介…

ant-design中a-table获取多分页中选择、全选问题处理

ant-design中<a-table获取多分页中选择、全选问题处理1、首先记录里需要有key ,用于标识行,不然如果各页中有重复key的话,切换到新页时会显示已选key的那行(key匹配) 2、在rowSelection中处理,关键是selected…

ant-design中a-table前端分页

ant-design中<a-table前端分页 数据已经全部在datasource2中了,每页大小固定5条,使用slice 切片获取每页数据

hyx_蓝桥杯C++学习_系列二

hyx_蓝桥杯C++学习_系列二一、递归的介绍 1. 概念 递归是函数直接或间接调用自身的过程 2. 两个关键要素终止条件:防止无限递归,类似于循环的终止条件防止死循环 递归表达式:递归的主体,将问题拆分为规模更小的子问…