65.9K
CodeProject 正在变化。 阅读更多。
Home

计算指标和使用 CodeDOM 进行搜索(第八部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2012年12月8日

CDDL

7分钟阅读

viewsIcon

22999

downloadIcon

745

计算 CodeDOM 上的指标并进行搜索。

引言

本文讨论 CodeDOM 中代码的基本分析,例如计算其指标和进行搜索。包含源代码。这是 CodeDOM 系列文章的第 8 部分。在前面的部分中,我讨论了 CodeDOMs提供了一个 C# CodeDOM,一个WPF UI 和 IDE,一个C# 解析器解决方案/项目 CodeDOM 类,涵盖了加载类型元数据,并处理了在 CodeDOM 中解析符号引用

访问者模式 

计算 CodeDOM 树(或子树)上的指标或搜索都需要遍历树中的对象。这需要每种对象类型的专业知识,因为不同的类型有不同的字段表示子对象——一个 BinaryOperatorLeftRight 子对象,一个 If 语句有一个子 Expression 等等。为了避免多次编写特定代码来访问子对象,采用了访问者设计模式。这是通过创建 IVisitor 接口并向 CodeObject 添加一个抽象的 void Accept(IVisitor visitor) 方法来完成的,该方法在所有子类中根据需要进行重写(所有定义子对象的子类)。为了以特定目的访问树中的节点,声明一个实现 IVisitor 的类,然后实例化该类并将其传递给所需树或子树的最顶层对象的 Accept() 方法。

IVisitor 接口为 CodeDOM 中更重要的类型定义了大约 55 个方法。一些叶子类型没有单独表示(例如具体运算符、引用和文档注释类型),以使方法计数更接近 50 而不是 300。如果必须检查没有自己方法的特定类型,则需要将逻辑添加到最近的基类型的方法中,该方法使用 isas 来检查特定类型。接口的所有方法都必须始终实现,但不需要特殊访问逻辑的类型可以将其方法留空或仅调用默认处理例程。内部生成(隐藏)的代码对象不会被访问(例如生成的默认构造函数、委托构造函数和 BeginInvoke/EndInvoke 方法、CodeUnits 的全局 extern 别名等)。使用 HiddenRef 属性的隐藏符号引用是可选访问的,具体取决于接口的 bool VisitHiddenRefs 属性的值。 

Nova.Examples 项目中添加了一个新示例 VisitTree(),它演示了如何使用 MyVisitor 类实现您自己的访问者。

计算指标

代码库上最基本的指标只是代码中所有不同类型对象的计数。例如,项目、文件、行、SLOC(源代码行——包含实际代码的行)、语句、循环、局部变量、字面量等。更高级的指标需要一些计算,例如每个方法/类的平均 SLOC、每个方法/类的平均条件、每个类的平均方法、每个方法/类的平均代码对象等。

为了实现度量功能,添加了一个 Metrics 类,其中包含许多字段,用于计算 CodeDOM 中不同类型的对象,以及一些用于计算度量的字段。已经创建了一个 MetricsVisitor 类,该类实现了 IVisitor 接口,并包含一个 Metrics 实例,该实例用于在访问树时计算各种类型的对象,并通过调用 'CalculateMetrics(CodeObject)' 并传入起始节点来激活。此方法在对象上调用 Accept(),它会更新所有对象计数,然后它还在 Metrics 实例上调用 Calculate() 以更新计算的度量。为了使此过程更简单,已将 'Metrics CalculateMetrics()' 方法添加到 CodeObject,该方法创建一个 MetricsVisitor 实例,在其上调用 CalculateMetrics(),并返回生成的 Metrics 对象——因此,只需在 CodeDOM 中的任何对象上调用 CalculateMetrics() 即可计算其度量。

当加载 SolutionProjectCodeUnit 时,如果指定了 LoadOptions.LogMetrics 选项,Load() 方法将自动在该对象上调用 CalculateMetrics() 并记录总项目(针对解决方案)、文件(针对解决方案/项目)、行、SLOC、类型和代码对象。Metrics 类的字段还具有 Description 属性,详细描述每个度量,并可由 UI 用于向用户显示(Nova Studio 现在就实现了这一点)。 Nova.Examples 项目中添加了一个新示例 CalculateMetrics(),它演示了如何计算度量。用法非常简单,例如:

// Calculate some metrics for an entire Solution:
Metrics metrics = solution.CalculateMetrics();
Log.WriteLine(string.Format("Solution '{0}': {1} projects; {2} files; {3} lines; "
    + "{4} SLOC; {5} types; {6} code objects; {7:N2} code objects per SLOC",
    solution.Name, metrics.Projects, metrics.Files, metrics.Lines, metrics.SLOC,
    metrics.Types, metrics.CodeObjects, metrics.CodeObjectsPerSLOC));

搜索 CodeDOM 

现在可以使用新的类 FindReferencesFindByTypeFindByText 来搜索代码中的特定引用、类型或文本,所有这些类都实现了 IVisitor 接口。还提供了一个新的类 Result,它表示找到的 CodeObject 及其关联的 CodeUnit。所有三个查找类都在给定作用域(起始 CodeObject)上工作,并生成 Result 对象的集合。FindReferences 类有各种选项,例如在搜索类型引用时包含成员引用、包含派生类型、包含虚成员的重写,或在搜索方法时包含重载。FindByText 类有选项可以区分大小写、仅匹配整个单词、使用正则表达式,或仅匹配声明、引用、字面量、注释或消息。

Nova.Examples 项目中添加了新示例(与类名相同),演示了如何使用这三个新类来搜索 CodeDOM。以下是一些示例用法:

// Find all references to a method in an entire solution
FindReferences findMethodReferences = new FindReferences(methodDecl, solution);
findMethodReferences.Find();
Log.WriteLine("Found " + findMethodReferences.Results.Count
    + " references to method declaration in the solution");


// Find all If statements in a CodeUnit
FindByType findIfs = new FindByType(typeof(If), codeUnit);
findIfs.Find();
Log.WriteLine("Found " + findIfs.Results.Count + " 'if' statements in "
    + codeUnit.Name);

// Find all code objects in a solution that have a name of 'find1' OR 'find2', or
// contain it in their text content.  Match case, whole words, and use reg exp.
FindByText findText = new FindByText("find1|find2", solution, true, true, true);
findText.Find();
Log.WriteLine("Found " + findText.Results.Count + " objects containing the "
    + "text 'find1' OR 'find2' in the solution");

FindByType 类已用于在 CodeObject 上实现一个名为 GetAllChildren<T>() 的辅助方法,该方法返回一个 IEnumerable<T>,可用于轻松遍历特定类型的所有子对象。例如:

// Query ALL MethodDecls in an entire Solution that have more than 2 'if' statements
var methodDecls = from methodDecl in solution.GetAllChildren<MethodDecl>()
                  where methodDecl.GetAllChildren<If>().Count() > 2
                  select methodDecl;
Log.WriteLine(methodDecls.Count() + " MethodDecls have more than 2 'if' statements");

Nova Studio 改进

Nova Studio 现在在解决方案树的上下文菜单上有一个“指标”选项,可以计算选定解决方案、项目、文件夹或单个文件的指标,并在新的“指标”对话框中显示它们。工具提示提供每个指标的详细说明。  还有一个工具栏图标,可以计算任何正在查看的文件的指标。对话框如下所示: 

代码窗口的上下文菜单现在有“查找”选项,带有新的“查找引用”和“按类型查找”对话框,工具栏上的“查找”图标会弹出一个新的“查找文本”对话框。 这些新对话框允许指定搜索范围和各种选项,结果显示在 Nova Studio 底部的新“结果”窗口中。对话框和“结果”窗口如下所示:

使用附加源代码

已添加一个新 Analysis 文件夹,其中包含新类 MetricsMetricsVisitorFindByTextFindByTypeFindReferencesResult。已将各种与分析相关的方法(例如 Accept())添加到许多现有 CodeDOM 类中,并分隔到带有“ANALYSIS”注释的区域中。Nova.Examples 项目中添加了新示例,利用了新功能:FindReferences()FindByType()FindByText()CalculateMetrics()VisitTree(),其中使用了新的 MyVisitor 类。此外,还添加了 LINQ 查询,它们使用 CodeObject.GetAllChildren<T>() 来查询特定类型的所有对象。 Nova Studio 已按照上一节所述进行了改进。与往常一样,提供了一个包含二进制文件的单独 ZIP 文件,以便您可以直接运行它们而无需先构建它们。

摘要

现在可以轻松计算 CodeDOM 的指标,使用访问者模式创建自定义代码来处理 CodeDOM,并在 CodeDOM 中搜索文本/类型/引用。 Nova Studio 已添加了从 UI 执行此类操作的支持。通过这一系列文章,已经构建了一个相当大的功能集,形成了一个有用的 C# CodeDOM。如果能添加静态代码分析来检测代码中可能存在的问题并提出改进建议,那就更好了,但我暂时不打算解决这个问题。在我的下一篇文章中,我将研究 Roslyn 项目,并了解它与我的 CodeDOM 的可用性、功能和性能相比如何。

© . All rights reserved.