tnblog
首页
视频
资源
登录

.net Roslyn 测试分析器

1036人阅读 2024/10/8 14:15 总访问:3467533 评论:0 收藏:0 手机
分类: .net后台框架

.net Roslyn 测试分析器


关于项目的创建请参考:https://www.tnblog.net/hb/article/details/8473

简单测试方式


首先打开我们的MyRoslynUnitTest测试类。


在头部引用相关的命名空间。

  1. using Microsoft.VisualStudio.TestTools.UnitTesting;
  2. using System.Threading.Tasks;
  3. using Microsoft.CodeAnalysis;
  4. using System.Collections.Immutable;
  5. using System;
  6. using System.Linq;
  7. using System.Reflection;
  8. using Microsoft.CodeAnalysis.Diagnostics;


然后我们通过在当前解决方案类中创建一个新的的项目,并在其中添加一个File.cs的类,把代码放入其中后,对其进行诊断。
具体代码如下:

  1. private static async Task<ImmutableArray<Diagnostic>> GetDiagnostics(string code)
  2. {
  3. // 创建一个 AdhocWorkspace 实例,用于处理代码分析过程
  4. AdhocWorkspace workspace = new AdhocWorkspace();
  5. // 获取当前工作空间中的解决方案
  6. var solution = workspace.CurrentSolution;
  7. // 创建一个新的项目 ID
  8. var projectId = ProjectId.CreateNewId();
  9. // 在解决方案中添加一个新项目,命名为 "MyTestProject"
  10. solution = solution
  11. .AddProject(
  12. projectId, // 项目 ID
  13. "MyTestProject", // 项目名称
  14. "MyTestProject", // 项目路径
  15. LanguageNames.CSharp); // 项目语言为 C#
  16. // 为项目添加一个包含代码内容的文档 "File.cs"
  17. solution = solution
  18. .AddDocument(DocumentId.CreateNewId(projectId), // 文档 ID
  19. "File.cs", // 文档名称
  20. code); // 文档内容,即传入的代码
  21. // 从解决方案中获取刚刚创建的项目
  22. var project = solution.GetProject(projectId);
  23. // 为项目添加一个元数据引用,引用系统的核心库 mscorlib (即 object 类所在的程序集)
  24. project = project.AddMetadataReference(
  25. MetadataReference.CreateFromFile(
  26. typeof(object).Assembly.Location))
  27. // 添加其他必要的引用,用于解析 ImmutableArray 类型的依赖项
  28. .AddMetadataReferences(GetAllReferencesNeededForType(typeof(ImmutableArray)));
  29. // 获取项目的编译结果
  30. var compilation = await project.GetCompilationAsync();
  31. // 创建一个包含自定义分析器的编译结果
  32. var compilationWithAnalyzer = compilation.WithAnalyzers(
  33. ImmutableArray.Create<DiagnosticAnalyzer>(
  34. new CreationAnalyzer())); // 使用 CreationAnalyzer 分析器
  35. // 获取并返回所有诊断结果
  36. var diagnostics = await compilationWithAnalyzer.GetAllDiagnosticsAsync();
  37. return diagnostics;
  38. }
  39. // 获取指定类型所需的所有元数据引用
  40. private static MetadataReference[] GetAllReferencesNeededForType(Type type)
  41. {
  42. // 获取该类型所需的所有程序集文件,并转换为元数据引用数组
  43. var files = GetAllAssemblyFilesNeededForType(type);
  44. return files.Select(x => MetadataReference.CreateFromFile(x)) // 创建元数据引用
  45. .Cast<MetadataReference>()
  46. .ToArray(); // 转换为 MetadataReference 数组
  47. }
  48. // 获取指定类型所需的所有程序集文件路径
  49. private static ImmutableArray<string> GetAllAssemblyFilesNeededForType(Type type)
  50. {
  51. // 获取该类型的程序集及其引用的所有程序集
  52. return type.Assembly.GetReferencedAssemblies() // 获取引用的所有程序集
  53. .Select(x => Assembly.Load(x.FullName)) // 加载程序集
  54. .Append(type.Assembly) // 包含自身的程序集
  55. .Select(x => x.Location) // 获取程序集文件路径
  56. .ToImmutableArray(); // 转换为 ImmutableArray<string>
  57. }


接下来我们定义一个EmptyMethodGeneratesNoDiagnostics测试方法,测试其中什么代码都不添加的情况下,诊断的数量为0

  1. [TestMethod]
  2. public async Task EmptyMethodGeneratesNoDiagnostics()
  3. {
  4. // 定义一段空的 Main 方法代码,不执行任何操作
  5. var code = @"
  6. public static class Program
  7. {
  8. public static void Main()
  9. {
  10. }
  11. }";
  12. // 调用 GetDiagnostics 方法,对代码进行诊断,返回诊断结果
  13. ImmutableArray<Diagnostic> diagnostics = await GetDiagnostics(code);
  14. // 断言诊断结果数量为 0,即空方法不应该产生任何诊断
  15. Assert.AreEqual(0, diagnostics.Length);
  16. }


然后我们定义一个CreatingAnImmutableArrayViaTheEmptyPropertyGeneratesOneDiagnostic测试方法,这里肯定会触发我们的警告,所以我们断言了它的警告ID是否为BadWayOfCreatingImmutableArray,以及触发的行数在第7行。

  1. [TestMethod]
  2. public async Task CreatingAnImmutableArrayViaTheEmptyPropertyGeneratesOneDiagnostic()
  3. {
  4. // 定义一段代码,使用 ImmutableArray<int>.Empty 创建并添加元素
  5. var code = @"
  6. using System.Collections.Immutable;
  7. public static class Program
  8. {
  9. public static void Main()
  10. {
  11. var array = ImmutableArray<int>.Empty.Add(1);
  12. }
  13. }";
  14. // 调用 GetDiagnostics 方法,对代码进行诊断,返回诊断结果
  15. ImmutableArray<Diagnostic> diagnostics = await GetDiagnostics(code);
  16. // 断言诊断结果数量为 1,即应该生成一个诊断信息
  17. Assert.AreEqual(1, diagnostics.Length);
  18. // 获取诊断结果中的第一个诊断信息
  19. var diagnostic = diagnostics[0];
  20. // 断言诊断的 ID 是 "BadWayOfCreatingImmutableArray"
  21. Assert.AreEqual(diagnostic.Id, "BadWayOfCreatingImmutableArray");
  22. // 获取诊断发生的位置
  23. var location = diagnostic.Location;
  24. // 获取诊断的位置行信息
  25. var lineSpan = location.GetLineSpan();
  26. // 断言诊断发生在第 7 行代码,即 ImmutableArray 的创建行
  27. Assert.AreEqual(7, lineSpan.StartLinePosition.Line);
  28. }


还定义了一个测试方法,由于我这里没有通过引用命名空间进行引用,而是直接通过命名空间点出来的类名,所以我们的分析根本找不到这个问题,也没办法弹出警告。自然在第一个断言时就报错。
代码如下:

  1. [TestMethod]
  2. public async Task CreatingAnImmutableArrayViaTheEmptyPropertyWithoutOpeningTheImmutableNamespaceGeneratesOneDiagnostic()
  3. {
  4. // 定义一段代码,使用完整命名空间 System.Collections.Immutable.ImmutableArray 创建并添加元素
  5. var code = @"
  6. public static class Program
  7. {
  8. public static void Main()
  9. {
  10. var array = System.Collections.Immutable.ImmutableArray<int>.Empty.Add(1);
  11. }
  12. }";
  13. // 调用 GetDiagnostics 方法,对代码进行诊断,返回诊断结果
  14. ImmutableArray<Diagnostic> diagnostics = await GetDiagnostics(code);
  15. // 断言诊断结果数量为 1,即应该生成一个诊断信息
  16. Assert.AreEqual(1, diagnostics.Length);
  17. // 获取诊断结果中的第一个诊断信息
  18. var diagnostic = diagnostics[0];
  19. // 断言诊断的 ID 是 "BadWayOfCreatingImmutableArray"
  20. Assert.AreEqual(diagnostic.Id, "BadWayOfCreatingImmutableArray");
  21. // 获取诊断发生的位置
  22. var location = diagnostic.Location;
  23. // 获取诊断的位置行信息
  24. var lineSpan = location.GetLineSpan();
  25. // 断言诊断发生在第 5 行代码,即使用完整命名空间创建 ImmutableArray 的代码行
  26. Assert.AreEqual(5, lineSpan.StartLinePosition.Line);
  27. }


我们可以通过判断它的命名符号(Symbol)来进行解决,当然验证的代码需要做一些修改。

  1. [DiagnosticAnalyzer(LanguageNames.CSharp)]
  2. public class CreationAnalyzer : DiagnosticAnalyzer
  3. {
  4. /// <summary>
  5. /// 定义诊断规则,包括诊断ID、标题、消息格式、分类、严重性等
  6. /// </summary>
  7. private static DiagnosticDescriptor descriptor =
  8. new DiagnosticDescriptor(
  9. "BadWayOfCreatingImmutableArray",
  10. "Bad Way Of Creating Immutable Array",
  11. "Bad Way Of Creating Immutable Array",
  12. "Immutable arrays",
  13. DiagnosticSeverity.Warning,
  14. isEnabledByDefault: true
  15. )
  16. ;
  17. public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
  18. => ImmutableArray.Create(descriptor);
  19. public override void Initialize(AnalysisContext context)
  20. {
  21. context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression);
  22. }
  23. private void Analyze(SyntaxNodeAnalysisContext context)
  24. {
  25. // 获取当前的节点
  26. var node = (InvocationExpressionSyntax)context.Node;
  27. // 我们肯定会根据 ImmutableArray<int>.Empty.Add(1); 找到这个特点
  28. // 我们看到了ArgumentList是有(1)值的,所以小于一个参数的跳过
  29. if (node.ArgumentList.Arguments.Count != 1) return;
  30. // 无法将表达式转换成成员、方法、属性的去掉
  31. // 一般找都是从右往左去找
  32. if (!(node.Expression is MemberAccessExpressionSyntax addAccess)) return;
  33. // 判断方法名是否胃Add
  34. if (addAccess.Name.Identifier.Text != "Add") return;
  35. // 获取上一个的成员、方法、属性
  36. if (!(addAccess.Expression is MemberAccessExpressionSyntax emptyAccess)) return;
  37. // 判断是不是Empty,不是就直接返回
  38. if (emptyAccess.Name.Identifier.Text != "Empty") return;
  39. // 判断符号类型的命名
  40. if (!(context.SemanticModel.GetSymbolInfo(emptyAccess.Expression).Symbol is INamedTypeSymbol imSymbol))
  41. return;
  42. // 检查符号的名称是否为 "ImmutableArray" 如果不是则返回
  43. if (imSymbol.Name != "ImmutableArray")
  44. return;
  45. // 泛型参数为1
  46. if (imSymbol.TypeArguments.Length != 1)
  47. return;
  48. // 获取符号所在的名称空间完整名称
  49. var fullnameOfNamespace = GetFullname(imSymbol.ContainingNamespace);
  50. if (fullnameOfNamespace != "System.Collections.Immutable") return;
  51. //// 判断是不是GenericNameSyntax类型的
  52. //if (!(emptyAccess.Expression is GenericNameSyntax ImmutableArrayAccess)) return;
  53. //// 判断是不是是否有一个泛型的类型
  54. //if (ImmutableArrayAccess.TypeArgumentList.Arguments.Count != 1) return;
  55. //// 判断是否是ImmutableArray
  56. //if (ImmutableArrayAccess.Identifier.Text != "ImmutableArray") return;
  57. // 创建提示的消息
  58. context.ReportDiagnostic(Diagnostic.Create(descriptor, node.GetLocation()));
  59. }
  60. private static string GetFullname(INamespaceSymbol containingNamespace)
  61. {
  62. // 如果是全局命名空间,返回空字符串
  63. if (containingNamespace.IsGlobalNamespace)
  64. return "";
  65. // 如果命名空间是全局命名空间的直接子命名空间,直接返回其中的名称
  66. if (containingNamespace.ContainingNamespace.IsGlobalNamespace)
  67. return containingNamespace.Name;
  68. return GetFullname(containingNamespace.ContainingNamespace)+"."+containingNamespace.Name;
  69. }
  70. }


再次测试我们就能够通过了。


将类型空间用一个变量做代替也是能够检测出来的,但是需要注意我们的第二次验证修改为第7行。


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

.net Roslyn的基本使用

.net Roslyn的基本使用[TOC] Roslyn简介Roslyn是C#和Visual Basic编译器的开源实现,具有用于构建代码分析工具的API表面。...

net core 使用 EF Code First

下面这些内容很老了看这篇:https://www.tnblog.net/aojiancc2/article/details/5365 项目使用多层,把数据库访问...

cAPS.net 保存base64位格式的图片

publicvoidUpload() { //取出图片对应的base64位字符 stringimgBase=Request[&quot;imgBase&quot;]; //c#里边的base6...

Quartz.net实例动态改变周期调度。misfire、Cron

Quartz:Java编写的开源的任务调度作业框架 类似Timer之类定时执行的功能,但是更强大Quartz.NET:是把Quartz转成C# NuGet...

.net Windows服务发布、安装、卸载、监听脚本。服务调试

一、脚本 为方便不用每次都去写安装卸载的脚本1.安装脚本@echooff @echo开始安装【服务】 %SystemRoot%\Microsoft.NET\Fr...

c、VB.net中全角半角转换方法

///&lt;summary&gt; ///转全角的函数(SBCcase) ///&lt;/summary&gt; ///&lt;paramname=&quot;input&quot;&gt;任意字符串...

.net mvc分部页,.net core分部页

.net分部页的三种方式第一种:@Html.Partial(&quot;_分部页&quot;)第二种:@{ Html.RenderPartial(&quot;分部页&quot;);}...

C.net 配合小程序实现经过第三方服务器中转文件

某些时候,微信小程序前段上传文件的时候需要经过第三方服务器再将文件上传到客户的服务器;操作如下:1:(小程序内向中端服...

.net实现QQ邮箱发送邮件功能

1、微软已经帮我们封装好了发送邮件的类MailMessage,MailMessage类构造一些邮件信息,然后通过SmtpClient进行邮件发送。Mai...

StackExchange.Redis操作redis(net core支持)

官方git开源地址https://github.com/StackExchange/StackExchange.Redis官方文档在docs里边都是官方的文档通过nuget命令下...

windows 自带的netsh进行端口映射

使用netsh 把本地任意ip的25566端口 映射到192.168.81.234的25565端口netshinterfaceportproxyaddv4tov4listenaddress=0.0....

确保.net程序始终以管理员身份运行

usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; ...

ASP.net Timer细节处理

Timer的用法:1:本人称之为计时器,是asp.net官方的一种。用法即是计时所用 2:关于计时有很多中方式,本人学识有限,暂...

.net core 使用session

tip:net core 2.2后可以直接启用session了,不用在自己添加一次session依赖,本身就添加了使用nuget添加引用Microsoft.AspN...

通俗易懂,什么是.net?什么是.net Framework?什么是.net core?

朋友圈@蓝羽 看到一篇文章写的太详细太通俗了,搬过来细细看完,保证你对.NET有个新的认识理解原文地址:https://www.cnblo...
这一世以无限游戏为使命!
排名
2
文章
634
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术