分析器:常见问题

这篇具有很好参考价值的文章主要介绍了分析器:常见问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

title: 分析器:常见问题
date: 2022-04-03
tags:
- C#
- .NET
- Roslyn

前言

源生成器(增量生成器)由于它特殊的定位,关于它的调试十分困难。在这里分享一些调试它的经验。

另外经常有写类库,然后提供可以生成代码的Attribute给用户的需求,此时需要用到传递引用的知识点。

调试源生成器

源生成器执行时间

源生成器项目和普通的项目不同。

普通的会在你按下运行或调试后才会运行;而源生成器会在两种情况下运行:

重新生成解决方案或该项目时候运行,运行后会生成dll文件。在下一次启动VS的时候,会连着dll一起读取,所以可能会有VS找不到生成的文件导致报错,但可以正常运行的问题,重启VS即可。

在生成项目后第二次及以后打开项目时,每次对代码进行更改都会重新运行源生成器的dll,并实时将生成的代码加入到项目中。所以源生成器的执行效率很大程度关乎用户的编程手感。

以下程序段默认引用命名空间:

using System.Diagnostics;

启动调试器

在源生成器项目中,直接在Visual Studio用鼠标点击行号左边打的(红色圆形的)断点是没有用的,需要添加一条Debugger.Launch();,表示启动了调试器。

如果程序运行到这条语句时,会弹出一个窗口,选择调试的程序:

分析器:常见问题

建议选择自己的项目(在此图的第二个)即可。

点击OK后程序会停在Debugger.Launch();处,此时可以插入Debugger.Break();或直接鼠标点击插入断点。

如果要启动调试器,Debugger.Launch();一定要放在源生成器刚开始的位置,而且不要插入多个,尤其不能插在多次执行的程序块内(如循环);若有需要可在其中打断点,否则第二次打开VS会一直弹窗。

生成中关闭调试器

有时运行一半时发现问题无需继续调试时,需要关闭调试器。但简单地终止调试可能无效,因为可能遇到另一个Debugger.Launch();

所以我们需要先停止生成,再关闭调试器。

  1. 生成→取消

  2. (如果调试器没有关闭)调试→停止调试

关闭Visual Studio前

关闭之前我们应该先把Debugger.Launch();删除或注释掉,并重新生成项目,以免下次打开VS的时候自动弹出调试器窗口。

类库中分析器的传递引用

从引用项目时的方式就可以看出,生成器项目本身是作为分析器(Analyzer)项目引入的:

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

此时类库项目引用了源生成器项目,类库项目又被用户项目引用,那么问题是用户项目可以被源生成器生成代码吗?

答案一般是不能。

如果要实现这种效果,那需要满足两个条件:

  1. 类库项目应作为NuGet包被引用,而非项目引用。

  2. 类库项目将分析器包含进NuGet包。

综上,我们应该在生成NuGet包的项目内这样写:

<ItemGroup>
    <ProjectReference Include="XXX.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    <None Include="XXX.SourceGenerator.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>

其中XXX.SourceGenerator.dll是源生成器项目的输出文件路径。

在引用该NuGet包之后,除了可以使用生成器外,其他该NuGet项目引用的类型也可以访问,这就是所谓“传递引用”。

其中ReferenceOutputAssembly="false"是指虽然引用分析器的输出,但不引用他的类型(如class XXXGenerator)。

注:如果项目没有使用NuGet包的必要,并且可以实现项目引用,又有此类需求;则可以简单地让用户项目按分析器引用源生成器项目即可。

分析器简介

本文分析器主要指以“Analyzer”模式引用的项目或NuGet包,例如源生成器(source generator)、代码分析器(analyzer)、代码修复器(codefixer)等

注:其中源生成器和代码分析器的“血缘关系”更近,可能因为都是.NET使用的分析器,而代码修复器是提供给Visual Studio使用的,所以关系疏远一些

分析器引用别的项目时的问题

根据MSDN和Visual Studio自带的代码示例,所有分析器和源生成器都必须包含以下两个库:

Microsoft.CodeAnalysis.CSharp
Microsoft.CodeAnalysis.Analyzers

所有代码修复器都必须包含以下这个库:

Microsoft.CodeAnalysis.CSharp.Workspaces

肯定不止我一个人(bushi)想到,为什么不把一些最简单的类型、或者一些工具库给所有的分析器共享呢?这样引用时就不需要写字符串而是nameof(xxx)了,多优雅。

可惜由于分析器项目的限制,除了以上说的必须包含的库,其他库都不能很方便地引用。但这也不是说每次写分析器都要手动实现.NET的新特性,Sergio Pedri[1]大佬实现了Poly#[2](PolySharp)库,这个库实现了绝大部分可以实现的特性,并且可以被分析器项目“引用”。因为它是把所有需要的代码生成到你的项目里,而非直接引用,所以绕过了分析器不能引用项目的限制。

引用这个库的方法和其他库一样,在NuGet里下载并添加包引用即可。

如果想要引用其他NuGet包(如System.Text.Json),可以参考我的这篇文章:《分析器/源生成器添加项目依赖的方式》

目标平台问题

分析器项目与普通项目不同,分析器本身的dll不会被用户项目直接引用,而是被编译平台(如.NET SDK或Visual Studio等)引用,从而对代码进行分析。所以分析器本身的dll不会被包含进用户项目生成的应用里(源生成器生成的代码会)

既然分析器的dll是被编译平台引用而非用户项目,那源生成器的目标平台就应该与编译平台有关,而非用户项目。例如,在x64架构的电脑上编译x86的应用程序,此时一般的用户项目目标平台都应该设置为x86,但分析器项目的目标平台却应该与电脑保持一致(x64),因为编译项目时,是电脑上x64的.NET SDK调用了分析器项目。

为了保证项目编译的可移植性,如何判断编译电脑的架构并设置为对应平台呢?此时简单地将目标平台设为AnyCPU即可,AnyCPU正是根据自身电脑架构获取的目标平台。

又如使用GitHub Action时,分析器项目经常会遇到如下错误(CS8034):

CSC : warning CS8034: Unable to load Analyzer assembly D:\a\xxx.SourceGenerator\bin\x86\Debug\netstandard2.0\xxx.SourceGenerator.dll : Unable to load xxx.SourceGenerator [D:\a\xxx.csproj]

这显然是因为目标平台设置错误,但使用Visual Studio手动编译就没有问题,sln文件里的平台映射(platform map)也确实是AnyCPU啊?这是因为解决方案的平台映射不一定会被所有地方遵循,最稳妥的方案是在csproj文件里指定目标平台。故在分析器项目中加上如下一行(PlatformTarget)即可:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        ...
        <PlatformTarget>AnyCPU</PlatformTarget>
        ...
    </PropertyGroup>
    ...
</Project>

不过为了直接运行也不出错,建议平台映射里的AnyCPU也要保留。

IDE兼容性问题

既然分析器是被编译平台调用,那分析器的Roslyn版本也要与编译平台一致。

所有源生成器和代码分析器都会引用以下的库。

Microsoft.CodeAnalysis.CSharp

而代码修复器则会使用

Microsoft.CodeAnalysis.CSharp.Workspaces

这两个库的版本都和Roslyn的版本相同(截至本文发布,是4.6.0),而Visual Studio的版本也是相关(VS2022的版本目前是VS 17.6.xxx)。所以如果使用了4.6.0的Roslyn,低于17.6.xxx版本的VS就无法使用了。例如VS2022的版本号是VS 17.xxx,VS2019的版本号是VS 16.xxx。如果编写的分析器需要有较为广泛的兼容性,可能需要降低版本,放弃新特性

分析器的调试方式

分析器在生成(运行)时,默认不会启动调试器,所以需要手动添加:

Debugger.Launch();

推荐重新生成时采用Debug模式,Release模式会优化代码,导致有些地方可能看不到需要的变量值。

代码分析器的调试方法和源生成器一样,但代码修复器就有些不同了,添加启动调试器代码并重新生成后,需要先重启VS,这样VS才会重新加载代码生成器(然而添加启动调试器重启后再修改代码修复器代码或者删除启动调试器,都不需要重启VS,只要重新生成一下即可,十分奇怪)。

然后将鼠标放到代码分析器提供的警告上,他会出现修复错误的提示(此处两条相同的提示一条来自于VS本身,一条来自于Resharper,并不是BUG):

分析器:常见问题

点击显示可能的修补程序后,这个框会一闪而过:

分析器:常见问题

然后跳出选择调试器的对话框,注意此处与源生成器或代码分析器不同,此处没有该解决方案本身而是别的打开的解决方案(如果有的话)或者新的VS实例,任选一个即可。我们点开后就可以开心地进行调试了。

目标框架问题

所有分析器项目的目标框架都必须是.NET Standard2.0。据说因为是VS正在由.NET Framework向.NET (Core)迁移,所以使用这个折衷的方式来兼容两方。如果有朝一日VS完全用.NET重写的话,我们就能用上完全版的分析器了。

感谢

感谢Huo Yaoyuan大佬[3]的无私耐心解答x,学习时参考了吕毅[4]大佬的博客


  1. Sergio Pedri ↩︎

  2. Poly# ↩︎

  3. Huo Yaoyuan ↩︎

  4. 吕毅博客 ↩︎文章来源地址https://www.toymoban.com/news/detail-788699.html

到了这里,关于分析器:常见问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ElasticSearch 基础(七)之分析器

    就 ES 基础部分来说这暂时就是最后一篇的文章,写完之后就会学习 MQ 了。本篇内容简单了解 ES 的分析器,最重要的还是根据自己需求去定制自定义分析器,自定义分析器自行了解,这里只是基础。其他比较重要的就是中文分词器了,只需要知道常用的几种中文分词器就可以

    2024年02月09日
    浏览(42)
  • Elasticsearch:搜索及索引分析器

    在我之前的文章 “Elasticsearch: analyzer”,我详细介绍了在 Elasticsearch 中的分析器。分析器在 Elasticsearh 中,它在索引文档的时候需要使用,同时,它也在搜索时,也需要针对搜索的文字进行分词。在今天的文章中,我们来详细介绍分析器是如何在索引及搜索时使用的。 可以在

    2024年02月05日
    浏览(84)
  • 词法分析器的设计与实现

    1.1、实验目的         加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。 1.2、实验要求         1)对单词的构词规则有明确的定义;      

    2024年02月13日
    浏览(53)
  • Lex 生成一个词法分析器

     lex 通过输入一个.l 文件生成一个lex.yy.c 文件,然后通过c 编译器编译成一个可执行的词法分析器。 该词法分析器扫描输入源文件,生成一个token 符号流给后面语法分析器使用。   .l 文件的结构, 分成三个部分,声明, 转换规则, 自定义规则。 三个部分由%%分割 声明段,

    2024年02月19日
    浏览(49)
  • Android Profiler 内存分析器使用

    Android Profiler是Android Studio的一部分,提供了一个集成的性能分析工具套件,包括内存分析。Android Profiler 工具可提供实时数据,帮助您了解应用的 CPU、内存、网络和电池资源使用情况。 在Android Profiler中,您可以查看内存使用情况的实时图表、堆转储快照、分析内存泄漏等,

    2024年02月08日
    浏览(53)
  • 编译原理-6-LR语法分析器

    自顶向下的、不断归约的、基于句柄识别自动机的、适用于LR(∗) 文法的、LR(∗) 语法分析器 只考虑无二义性的文法 自底向上 构建语法分析树 根节点 是文法的起始符号 S S S 每个中间 非终结符节点 表示 使用它的某条产生式进行归约 叶节点 是词法单元$w$$ 仅包含终结符号与

    2024年02月05日
    浏览(48)
  • 编译原理实验三:预测分析法语法分析器的设计

    ​ 根据文法编制预测分析法语法分析程序,以便对输入的符号串进行语法分析。通过编写预测分析法语法分析程序掌握预测分析法的基本原理、FIRST和FOLLOW集的计算、预测分析表的构造方法以及语法分析法主控程序的设计。 对于给定的上下文无关文法,编程完成以下功能:

    2024年02月05日
    浏览(55)
  • 编译原理词法分析器(C/C++)

            词法分析器不用多说,一开始我还不知道是什么样的,看了下别人的博客,再看看书,原来是输出二元组,这不就是字符串操作嘛。然后细看几篇博客,发现大都是用暴力判断来写的。我对代码重复性比较高的方法不太感冒,不是说我编程有多好,就是单纯的不喜欢

    2024年02月06日
    浏览(60)
  • 【编译原理】 实验一:词法分析器的自动实现(Lex词法分析)

    相关代码实操移步视频 https://www.bilibili.com/video/BV13x4y1o7FL 1.借助词法分析工具Flex或Lex完成(参考网络资源) 2.输入:高级语言源代码(如helloworld.c) 3.输出:以二元组表示的单词符号序列。 通过设计、编制、调试一个具体的词法分析程序,加深对词法分析原理的理解,并掌握

    2024年02月08日
    浏览(56)
  • Elasticsearch 分词器切词器分析器

    normalization : 文档规范化 先切词,然后规范化. 规范化要规范哪些内容? 大小写; 标点符号; 时态; 复数; 规范化主要是为了匹配更精准 character filter : 字符过滤器. 标点符号 分词之前的预处理,过滤无用字符 HTML Strip Character Filter :html_strip 参数:escaped_tags 需要保留的html标签 Map

    2024年02月12日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包