我应该为 C# 或 .NET 构建分析器吗?

编辑:与下面帖子的结论不同,我将分析器基于 C# CaaS 而不是 .NET IL。它现在可以使用

https://devsnicket.com/eunice/#csharp。后者不保留 dll 或 pdb 中类成员的顺序。虽然,在查看了一些开源 C# 项目之后,

默认情况下并未使用此功能

我一直在开发的名为 Eunice的软件开发工具是 从 JavaScript 分析开始的。它旨在支持多个可互换的分析仪,重复使用该工具的测量、图形和交互式组件。这些其他组件也是用 JavaScript 编写的,因此它们是跨平台的。在开发的早期阶段,我认为遵循dogfooding的做法是有益的 。要了解它的外观,您可以 在此处查看 Eunice 对其自身的分析并与之互动。

Eunice 现在已经成熟,我决定编写另一个分析器。凭借在 C# 和 .NET 方面的个人经验,我认为接下来为他们创建一个分析器对我来说会很有成效,对其他人也很有用。

C# 还是 .NET?

绝大多数 .NET 是使用 C# 编写的,其中包括 CaaS(编译器即服务)。使用为源文件提供的句法/语义模型将允许分析器表示软件在开发人员工作时的显示方式。

使用 C# 编写的软件也可以以包含 CIL(通用中间语言)的已编译汇编文件 (.dll) 的形式提供。这有一组更受限制的指令,但结果更冗长。如果这种冗长不会增加分析器的复杂性,那么受限的指令集可能会简化它。编译时不包括源代码中的一些信息,不是运行它所必需的;但是,此信息可以保存在随附的文件 (.pdb) 中(例如用于调试)。

我相信使用 C# CaaS 比使用编译后的 CIL 附带的调试文件更有效率。如果实现分析器所需的信息可以从 CIL 中轻松获得(即没有调试文件),那么这将更有效率。

源代码目录和命名空间

嵌套组形式的结构,可以在源代码目录和命名空间中指定。通常使用这两种并行构建软件的方式,但不必匹配。

它可以同时使用两者,具有与其他任何地方匹配的结构,然后使用一个插入另一个不存在的其他组。即使以这种方式限制差异,在导航代码库时,这种变化仍然会增加混淆的可能性。

如果结构不同,同时表示两者将需要 4 个维度 (2 x 2D) 并且会令人困惑。为了在分析仪中避免这种情况,将选择一个而不是另一个。分析器将使用命名空间,因为命名空间在 C# 中用于引用依赖项而不是文件路径。命名空间包含在 CIL 中,因此只需要 .NET 分析而不需要 C#。作为参考,源代码路径不包含在 CIL 中,但可从随附的调试文件中获得。

语言特征

一些 C# 语言功能在 CIL 中表示,具有原始代码中没有的附加结构。其中一些附加结构将仅包含生成的指令;但是,在其他情况下,原始 C# 片段的编译器输出将放置在这些生成的结构中。CIL 具有元数据以将结构标记为使用属性生成的编译器。无论属性是否存在或方法是否完全生成,可能仍然存在需要在分析中包含的依赖项。

我已经创建了下面的 C# 功能表和列表以及它们在 CIL 中的表示方式:

C# 1

未在 CIL 中表示:

  • 内置类型关键字
  • 行指令
  • 地区指令
  • 使用指令

C# 2

C# 3

C# 5

C# 6

未在 CIL 中表示:使用指令(静态)

C# 7

C# 8

* 名称需要推断/重新格式化

结论

上面所有需要 C# 名称的功能,在 CIL 中都有一个项目,它或它的派生是可用的。匿名方法和 lambdas 可能有依赖关系,但它们会被添加到代表其父类的项目中。

委托和枚举未标记为编译器生成,并且没有任何需要表示的实现(例如子项或依赖项)。

几个特性在生成的类的生成方法中移动和混合表示原始 C# 的 CIL。但是,这些遵循可以忽略所生成内容的依赖关系的模式。

Region 和 using 指令是上面列出的两个特性,在 CIL 中没有表示,在 C# 中非常明显。区域可以在分析中表示为额外级别的项目分组,这样做将匹配它们在 C# 文件中的显示方式。尽管区域的使用是有争议的,但将它们包含在 Eunice 中将是其特征的有用证明。使用指令及其指定别名的能力对 c# 的潜在依赖范围和详细程度有显着影响。

基于上述发现,分析器的开发速度将优先于包含区域和使用指令分析。我认为这两个特性是有价值的,但是如果将来要开发一个基于 C# CaaS 的分析器,将它们包含在一个基于 C# CaaS 的分析器中会更有效率。

Graham Dyson –  Eunice的创造者