前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Roslyn 对 C# 代码进行语义分析

使用 Roslyn 对 C# 代码进行语义分析

作者头像
walterlv
发布2023-10-23 11:13:50
3660
发布2023-10-23 11:13:50
举报

Roslyn 是微软为 C# 设计的一套分析器,它具有很强的扩展性。以至于我们只需要编写很少量的代码便能够分析我们的源代码。之前我写过一些使用 Roslyn 进行语法分析的文章。使用语法分析,可以轻松为代码编写提供各种错误报告以及修改代码(见这里)。而使用语义分析,你可以像在运行时使用反射一样,在编译时访问源代码中的各种类型、属性、方法等,特别适合用来分析引用、生成代码等。当然,实际项目里面将两者结合起来可以做到更多的效果。


本文将以 dotnetCampus.Ipc 项目里的自动生成 IPC 代理类型作为示例,来介绍如何使用 Roslyn 进行语法分析。本文会更偏碎片化。

第一步:找到编译信息和语法树

在开始后面的实际语义分析之前,你需要先拿到以下对象的实例:

  • Microsoft.CodeAnalysis.SyntaxTree:包含单个文件里所有语法节点的语法树
  • Microsoft.CodeAnalysis.Compilation: 包含整个编译项目的编译信息

你有以下方法可以拿到这些对象。

如果你正在编写代码分析器(Analyzer)和修改器(CodeFix)

代码分析器和修改器的入口方法可以得到一个 SyntaxNodeAnalysisContext 类型的参数,这个参数里面就可以拿到 Compilation 的实例。

同时,在这个入口方法中,你也很容易就得到一个语法节点“SyntaxNode”的实例,而每一个语法节点都有 SyntaxTree 属性可以拿到语法树。

关于代码分析器(Analyzer)和修改器(CodeFix)可以参考我之前的这些博客:

如果你正在编写代码生成器(Generator)

代码生成器的入口方法带有一个 GeneratorExecutionContext 类型的参数,而它直接就有我们需要的两种对象。

  • GeneratorExecutionContext.Compilation 即整个项目的编译信息;
  • GeneratorExecutionContext.Compilation.SyntaxTrees 包含整个项目正在参与编译的所有非生成器生成的代码的语法树。

第二步:获取语义模型和语义符号

使用这句,可以拿到一个语法树的语义模型:

1

var semanticModel = compilation.GetSemanticModel(syntaxTree);

通过这个语义模型,你可以找到每一个语法节点所对应的语义符号到底是什么。

接下来的部分,你需要先拥有 Roslyn 语法分析的基本能力才能完成,因为要拿到一个语义符号,你需要先拿到其对应的语法节点(至少是第一个节点)。例如,拿到一个语法树(SyntaxTree)中的类型定义,可以用下面的方法:

1 2 3 4

// 遍历语法树中的所有节点,找到所有类型定义的节点。 var classDeclarationSyntaxes = from node in syntaxTree.GetRoot().DescendantNodes() where node.IsKind(SyntaxKind.ClassDeclaration) select (ClassDeclarationSyntax) node;

这样,针对这个语法树里面的每一个类型定义,我们都可以拿到其对应的语义了:

1 2 3 4 5 6 7

foreach (var classDeclarationSyntax in classDeclarationSyntaxes) { if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is { } classDeclarationSymbol) { // 在这里使用你的类型定义语义符号。 } }

第三步:使用语义模型

经过了前两个步骤,Roslyn 语义分析最难的部分就结束了(没错,两句代码就结束了)。

接下来对语义符号的使用你可以简单想象成就是在使用反射功能的编译形式而已。你可以简单地获得类型的命名空间,获得类型的特性(Attribute);获得类型的成员,成员的特性……

1 2

// 获取类型的命名空间。 var namespace = classDeclarationSymbol.ContainingNamespace;

1 2 3

// 获得基类,获得接口。 var baseType = classDeclarationSymbol.BaseType; var interfaces = classDeclarationSymbol.Interfaces;

1 2

// 获取类型的成员。 var members = classDeclarationSymbol.GetMembers();

1 2 3 4 5 6 7 8 9 10

// 获取成员的类型,然后忽略掉属性里面的方法。 foreach (var member in members) { if (member is IMethodSymbol method && method.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet) { continue; } // 其他成员。 }

1 2

// 获得方法的形参数列表。 var parameters = method.Parameters;

1 2

// 获得方法的返回值类型。 var returnType = method.ReturnType;

还有更多。由于使用起来非常地直观而简单,所以就自己探索吧!


参考资料

本文会经常更新,请阅读原文: https://cloud.tencent.com/developer/article/2349815 ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一步:找到编译信息和语法树
    • 如果你正在编写代码分析器(Analyzer)和修改器(CodeFix)
      • 如果你正在编写代码生成器(Generator)
      • 第二步:获取语义模型和语义符号
      • 第三步:使用语义模型
      相关产品与服务
      腾讯云代码分析
      腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档