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

使用 StyleCop 进行 C# 代码审查 - 详细文章

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (32投票s)

2008年11月7日

CPOL

5分钟阅读

viewsIcon

165790

downloadIcon

1231

这是一篇关于使用 StyleCop 进行 C# 代码审查的详细文章。

目录

引言

在本文中,我们将讨论一个即将推出的代码审查工具 StyleCop。我们将了解其基础知识,并进行一个小型的代码审查实践,以了解 StyleCop 的工作原理。

您可以阅读我上一篇关于使用 FXCOP 进行代码审查的文章 《使用 FXCOP 进行代码审查》

安装和下载 StyleCop

StyleCop 使用一套规则来分析 C# 代码,以检查代码是否符合规则。您可以从 此链接 下载 StyleCop。如果您点击下载部分,您会看到三个安装程序,一个是安装软件的 StyleCop 设置,另外两个是 CHM 帮助文件。一个帮助文件是关于规则的,另一个是关于 StyleCop 提供的 API 的。

安装 StyleCop 后,在开始菜单中不会看到任何内容。当您打开任何 C# 项目并在项目上单击鼠标右键时,您应该会在项目中看到两个菜单,如下图所示 - 一个是帮助我们选择规则的 Stylecop 设置菜单,另一个是用于在 C# 项目上运行这些规则的菜单。

如果您直接运行 stylecop,您应该会在输出窗口中看到损坏的规则,如下图所示。

FXCOP 与 StyleCop 对比

FXCOP StyleCop
它运行在已编译的 DLL 上。 它运行在实际源代码上。
由于它运行在已编译的 IL 代码上,因此可以用于 C#、VB.NET,简而言之,任何编译为 IL 代码的语言。 目前它仅适用于 C#。

目前,我只能想到两个区别。如果有人知道更多,请告诉我,以便我更新。许多人正在询问关于这两个代码审查工具的比较表。

使用 FXCOP 进行代码审查的问题

首先,我要感谢 Thomas Wellar 提供了关于 FXCOP 代码审查问题的准确信息。

FXCOP 在实际编译的 DLL 上工作。例如,下面所示的 switch 方法可以被 .NET 编译器编译成一个 `string` 的字典。因此,我们实际上无法衡量代码具有什么样的质量。从实际源代码到 IL 的转换是非线性的。这就是 StyleCop 的优势所在,因为它直接操作源代码。它目前唯一的缺点是它只适用于 C#。

StyleCop 代码解析逻辑

StyleCop 有两个重要概念 - 一个是文档(document),另一个是元素(elements)。文档是文件,元素是文件内的所有内容。

例如,下面是一段简单的源代码,其中包含两个方法、属性和变量。

因此,stylecop 将文档中的所有内容视为一个元素。现在,让我们通过在 stylecop 中实现自定义规则来理解 stylecop 如何遍历文档。

创建自定义规则类

首先要做的是导入 stylecop 命名空间。请确保您从 *c:\program files\Microsoft Style Cop* 文件夹中添加引用。

using Microsoft.StyleCop.CSharp;
using Microsoft.StyleCop;

所有 stylecop 自定义规则类都应该继承自 `SourceAnalyzer` 类:

[SourceAnalyzer(typeof(CsParser))]
public class MyRules : SourceAnalyzer

当 style cop 开始运行时,它首先会执行 `analyzedocument` 方法。因此,重写该方法,并将我们的逻辑放在其中。

public override void AnalyzeDocument(CodeDocument document)
{}

在 analyze document 中,我们将文档强制转换为 `CsDocument` 类。正如我们之前所说,stylecop 会遍历文档,然后遍历每个元素。它通过使用委托和访问者模式来实现这一点。`WalkDocument` 方法会访问源代码中每个元素的 `VisitElement`。如果您对访问者模式感到好奇,可以随时阅读 这篇文章

CsDocument document2 = (CsDocument)document;
if (document2.RootElement != null && !document2.RootElement.Generated)
{
document2.WalkDocument(new CodeWalkerElementVisitor<object>
				(this.VisitElement), null, null);
}

`WalkDocument` 方法中定义的 `VisitElement` 是一个 `delegate`。因此,我们也需要定义该 `delegate`。您可以在下面的代码中看到我们是如何定义 `delegate` 并使用 `element` 对象来检查它是否是 `method`。如果 `element` 是一个 `method`,那么您可以执行一些逻辑。如果存在任何违规行为,我们需要使用 `AddViolation` 方法将其添加到违规集合中,如下面的代码片段所示:

private bool VisitElement(CsElement element, CsElement parentElement, object context)
{
if (element.ElementType == ElementType.Method)
{ this.AddViolation(parentElement, "MyCustomRule", "BlockStatementsShouldNotBeEmpty");
}
}

我们传递给规则的值之一是 `MyCustomRule` 值。这不过是一个 XML 文件。下面是一个 GOTO 示例规则的样本。编译 DLL 时,需要确保将此 XML 文件编译为嵌入式资源。

<SourceAnalyzer Name="Custom Rules">
<Description> Custom rules added to analyzer. </Description>
<Rules>
<RuleGroup Name="Database">
<Rule Name="MyCustomRule" CheckId="HE3333">
<Context>Always close the connection object</Context>
<Description>Always close the connection object</Description>
</Rule>
</RuleGroup>
</Rules>
</SourceAnalyzer>

编译 DLL 后,您需要将 DLL 放在 “*C:\Program Files\Microsoft StyleCop 4.3\*” 文件夹中。如果一切正常,您应该会在规则对话框中看到规则,如下图所示。

让我们来实际操作一下

所以,让我们来实际操作一下。假设我们想检查任何连接对象打开后是否应该关闭。那么,让我们在 `VisitElement delegate` 中编写一些好的逻辑。

我们首先要做的是定义两个变量 - `boolFoundConnectionOpen`,表示连接对象已打开,以及 `boolFoundConnectionClosed`,表示连接对象已关闭。

bool boolFoundConnectionOpen=false;
bool booFoundConnectionClosed=false;

在 `VisitElement delegate` 中,我们将元素文档强制转换为 `CsDocument` 类型,并将源代码读取到 `TextReader` 中。

CsDocument document3 = (CsDocument)element.Document;
System.IO.TextReader objReader = document3.SourceCode.Read(); 

好的,现在我们浏览 `reader` 对象并开始读取代码。如果我们找到 “`.Open`”,我们将 `boolFoundConnectionOpen` 设置为 `true`。如果我们找到相应的 `close`,我们将 `boolFoundConnectionClosed` 设置为 `true`。

while ((strCode = objReader.ReadLine()) != null)
{
if (strCode.Contains(".Open();"))
{
boolFoundConnectionOpen = true;
}
if(boolFoundConnectionOpen)
{
if (strCode.Contains(".Close();"))
{
booFoundConnectionClosed = true;
}
}
}

现在是最后一步。如果我们发现连接已打开但未关闭,我们只需将规则添加到违规集合中。

if ((boolFoundConnectionOpen) && (booFoundConnectionClosed==false))
{
this.AddViolation(parentElement, "MyCustomRule", "BlockStatementsShouldNotBeEmpty");
}

运行并享受……您可以在下面的图中看到我们没有关闭连接。您可以看到错误是如何在输出窗口中显示的。

进一步阅读,请观看下面的面试准备视频和分步视频系列。

© . All rights reserved.