首页 > 其他分享 >分析器:常见问题

分析器:常见问题

时间:2024-01-14 22:00:26浏览次数:26  
标签:常见问题 项目 生成器 分析器 VS 引用 调试器

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里下载并添加包引用即可。

目标平台问题

分析器项目与普通项目不同,分析器本身的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):

analyzer

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

codefix

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

choose_debugger

目标框架问题

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

感谢

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


  1. Sergio Pedri ↩︎

  2. Poly# ↩︎

  3. Huo Yaoyuan ↩︎

  4. 吕毅博客 ↩︎

标签:常见问题,项目,生成器,分析器,VS,引用,调试器
From: https://www.cnblogs.com/pokersang/p/17964221

相关文章

  • Maven常见问题与原理技巧
     背景目前项目中主流的都是使用maven等构建工具,当然在使用过程中也会遇到各种各样的疑惑或问题,比如:maven生命周期到底有啥用jar包下载不了怎么办不知道配置怎么配,下载jar包的顺序是什么jar包冲突又是怎么解决问的人多了,也就形成了大家的共性问题,这里基于这些问题,本文......
  • Hive常见问题
    Hive常见问题1、hive创建数据库在使用hive指令创建数据库时,指定创建位置createdatabasebigdata_demo1location'/bigdata_29';在shell脚本中显示创建返回结果但是在hdfs中并未找到该数据库;这里可能是bug,于是我们通过第二种方法,即在location中将该数据库也加入到路径中......
  • LiveGBS流媒体平台GB/T28181常见问题-国标编号是什么设备编号和通道国标编号标记唯一
    LiveGBS国标GB28181中国标编号是什么设备编号和通道国标编号标记唯一的摄像头|视频|镜头通道1、什么是国标编号?2、国标设备ID和通道ID3、ID统一编码规则4、搭建GB28181视频直播平台1、什么是国标编号?国标GB28181对接过程中,可能有的小伙伴会疑惑编号的问题。比如设备ID、视频通道......
  • LiveGBS流媒体平台GB/T28181常见问题-如何配置快照目录快照存储默认目录目录如何配置
    LiveGBS流媒体平台GB/T28181常见问题-如何配置快照目录快照存储默认目录目录如何配置1、快照目录2、指定快照目录3、搭建GB28181视频直播平台1、快照目录部署LiveGBS后,再查看通道播放后或是获取通道快照后,就会在LiveSMS部署的服务器里面存储对应的最新快照,默认的快照目录是Li......
  • LiveNVR监控流媒体Onvif/RTSP常见问题-如何配置快照目录快照存储默认目录目录如何配置
    LiveNVR监控流媒体Onvif/RTSP常见问题-如何配置快照目录快照存储默认目录目录如何配置?1、快照目录2、指定快照目录3、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务1、快照目录部署LiveNVR后,配置通道上线后,会在LiveNVR部署的服务器里面存储对应的最新快照,默认的快照目录是LiveNVR解......
  • Windows电脑多开软件的常见问题及解决方法
    Windows电脑多开软件的常见问题及解决方法在日常使用Windows电脑时,我们经常会遇到需要同时打开多个相同软件的情况,比如同时打开多个浏览器窗口、多个文档编辑器等。为了解决这一需求,有许多多开软件应运而生。然而,一些用户在使用多开软件时可能会遇到一些常见问题。本文将就Window......
  • ReactNative 常见问题及处理办法(加固混淆)
    ReactNative常见问题及处理办法(加固混淆)文章目录摘要引言正文ScrollView内无法滑动RN热更新中的文件引用问题RN中获取高度的技巧RN强制横屏UI适配问题低版本RN(0.63以下)适配iOS14图片无法显示问题RN清理缓存RNnavigation参数取值podinstall或者npminstall443问题处......
  • Layui官网常见问题
    应该如何加载模块?JSlayui.use(['layer','form','element'],function(){varlayer=layui.layer,form=layui.form,element=layui.element//……//你的代码都应该写在这里面});为什么表单不显示?当你使用表单时,layui会对select、checkbox、radio......
  • 奇迹常见问题如何解决
    1.登陆游戏时右上角提示与服务器连接中断你的服务器名称不一致02.登陆游戏时中间弹出与服务器连接中断IP设置错误了,请检查DMuServerCsData里面文件的设置03.我自己能进入网站,但是别人进不来1.你的系统有防火墙(网络连接-本地连接-属性-高级)这里关掉如果有多个连接需要全部修改(......
  • 服务器常见问题以及处理方案
    服务器在互联网、数据存储与共享、数据库管理、应用程序支持和安全性方面发挥着至关重要的作用。它们为用户提供了高效、安全、可靠的计算和存储能力,推动了现代社会的发展和进步。随着技术的不断进步,服务器的作用将变得更加广泛和重要。因此,管理人员需要对常见故障有一定的了解,并......