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

Visual Studio 2015 中的代码分析器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (12投票s)

2016 年 5 月 6 日

CPOL

12分钟阅读

viewsIcon

42510

downloadIcon

470

在本文中,我们将介绍 Visual Studio 2015 中另一个有趣且非常有用的功能:实时静态代码分析。

引言

这是“深入了解 Visual Studio 2015”系列中的下一篇文章。在系列的第一篇文章中,我们学习了 Visual Studio 代码建议和代码优化功能。您可以阅读有关代码建议的更多内容,请参见第 1 天。在本文中,我们将介绍 Visual Studio 2015 中另一个有趣且非常有用的功能:实时静态代码分析。该功能本身的名称就具有自我解释性。Visual Studio 使开发人员能够了解优化技术以及在编码过程中出现的编译时错误。也就是说,开发人员不必一遍又一遍地重新编译代码来了解编译时错误或代码优化,他可以在编码时即时查看所有这些内容并立即修复。让我们通过实际示例详细介绍该主题。在本文中,我们将介绍 Visual Studio 2015 中另一个有趣且非常有用的功能:实时静态代码分析。该功能本身的名称就具有自我解释性。Visual Studio 使开发人员能够了解优化技术以及在编码过程中出现的编译时错误。也就是说,开发人员不必一遍又一遍地重新编译代码来了解编译时错误或代码优化,他可以在编码时即时查看所有这些内容并立即修复。让我们通过实际示例详细介绍该主题。

学习 Visual Studio 2015 系列

实时静态代码分析

实时静态代码分析不是一个句子或短语;每个词都有独特的含义,为整个功能增加了价值。实时意味着您可以在键入代码的同时即时获得代码分析。您无需显式编译代码即可获得实时代码检查功能。静态意味着在代码未运行时对其进行分析。此功能旨在节省大量编码开发时间。代码在您键入时通过预定义分析器进行检查,您不必一遍又一遍地构建/编译应用程序。此外,这些分析器(除了显示错误外)在编码时还会提供某些警告或建议,以提高开发人员的编码技能并使其能够遵循最佳编码实践。这些内置分析器基于一组特定的规则工作,在键入代码时会根据这些规则进行验证。

可以通过安装在 Visual Studio 中的 NuGet 包来获得分析器。Visual Studio 2015 中有许多分析器。在早期版本的 Visual Studio 中,可用的分析器非常少,但现在您可以为 Visual Studio 开发中使用的每一种方法获得分析器。有适用于 Azure、单元测试、异常处理、模拟、ORM 等的分析器。您可以根据需要选择分析器。

此功能提供的最佳之处在于,我们可以针对所需的代码类型构建自己的分析器。这意味着我们可以定义自己的编码规则集,这些规则将在您或其他开发人员正在编写的实时代码上调用。规则可能包括警告、错误、建议、自动代码修复等。让我们通过一些示例来了解如何利用此功能。

代码分析器



创建一个包含简单算术运算方法的控制台应用程序。我在本文系列的第 1 天中创建了一个,并将继续使用它。本文使用的控制台应用程序名为 VS2015ConsoleApplication。它包含一个名为 ICalculator 的接口和一个名为 Calculator 的类,该类继承自上述接口,如下所示。

ICalculator

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Threading.Tasks;
   6:  
   7: namespace VS2015ConsoleApplication
   8: {
   9:     interface ICalculator
  10:     {
  11:         int Add(int a, int b);
  12:         int Subtract(int a, int b);
  13:         int Multiply(int a, int b);
  14:         float Divide(float a, float b);
  15:     }
  16: }

计算器

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Threading.Tasks;
   6:  
   7: namespace VS2015ConsoleApplication
   8: {
   9:     public class Calculator : ICalculator
  10:     {
  11:         public int Add(int a, int b)
  12:         {
  13:            throw new NotImplementedException();
  14:         }
  15:  
  16:         public float Divide(float a, float b)
  17:         {
  18:             throw new NotImplementedException();
  19:         }
  20:  
  21:         public int Multiply(int a, int b)
  22:         {
  23:            throw new NotImplementedException();
  24:  
  25:         }
  26:  
  27:         public int Subtract(int a, int b)
  28:         {
  29:             throw new NotImplementedException();
  30:         }
  31:     }
  32: }
  33:

如您所见,代码量不多。我们将使用此代码来检查和理解实时静态代码分析的功能和概念。

在 Visual Studio 中右键单击项目文件,然后从上下文菜单中选择“管理 NuGet 包...”选项。

单击此选项后,IDE 中将打开一个窗口,您可以在其中选择已安装的包、现有包的更新或浏览新的 NuGet 包。使用搜索框搜索名为“Analyzer”的关键字,如下所示。

您会看到窗口中出现许多代码分析器。单击它们时,它们会在右侧窗口中显示描述。在这里,我们可以选择将所需的任何分析器与现有项目一起使用。我们将使用其中一个分析器来检查其功能。我选择了“codecracker.CSharp”分析器进行进一步的解释和详细说明。向下滚动到此分析器,单击 codecracker.CSharp,然后通过单击安装按钮进行安装,如下面的图像所示。

安装完成后,Visual Studio 中的程序包管理器控制台将显示安装成功的消息,如下所示。

在 Visual Studio 的“引用”下,将出现一个新的引用,名为“Analyzers->CodeCracker.Common,CodeCracker.CSharp”。Analyzers 是根引用,其中可以包含多个分析器。

此外,还将添加一个新的“packages.config”文件,其中包含以下代码,其中包含包的信息。

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <packages>
   3:   <package id="codecracker.CSharp" version="1.0.0" targetFramework="net45" />
   4: </packages>

由于我们可以看到 packages.config 文件和解决方案中添加的引用,这意味着我们从 NuGet 下载的分析器已成功安装到控制台应用程序项目中。您可以通过单击 Visual Studio 中引用下的分析器箭头来检查所选分析器的所有规则集。单击“CodeCracker.CSharp”,然后获取此集合包含的所有消息、信息、建议、警告和错误列表。

每个规则都具有特定的语法,例如与之关联的唯一代码、带有警告、错误、信息、建议等描述性符号以及描述性文本。例如,如果我们检查第一条规则,它表明这是一种警告/建议,其唯一代码为 CC0001,描述为“只要有可能,就应该使用‘var’”。这意味着,在编码过程中,如果出现违反此规则的情况,我们将看到类似的消息以及警告符号。

代码分析

让我们编写一些代码来检查分析器的工作原理。以下是我编写的一些代码,用于在 Calculator 类中实现 Divide 方法。

   1: public float Divide(float a, float b)
   2:        {
   3:            try
   4:            {
   5:                float result;
   6:                result = a / b;
   7:                Console.WriteLine(String.Format("Results", result));
   8:                return result;
   9:  
  10:            }
  11:  
  12:            catch { }
  13:  
  14:        }

上述代码块故意写成可能导致一些编译时错误的方式。编写代码时,请保持“错误列表”窗口打开。您会注意到,在编写代码时,每当您违反任何代码分析器规则时,它都会立即显示在“错误列表”窗口中。这表明您在编码时即时获得了反馈和优化选项,并且无需依赖编译代码。例如,请看下图。我已编写代码,但尚未编译项目。

我们看到显示了两个警告,这意味着在编写代码时,我们违反了已安装代码分析器的两条规则。第一个说这是一个警告,代码为 CC0111,描述为“‘String.Format’方法的参数数量不正确”。我故意这样写代码是为了解释代码分析器的工作原理。我们可以看到并比较,错误列表中显示的规则与右侧代码分析器规则列表中的规则完全匹配。这证明了代码分析器按预期工作。在这里,我们看到无需编译应用程序,并且在修复代码后,代码分析器的错误/警告就会消失。我故意留空了 catch 块,以便代码分析器可以根据其定义的 catch 块规则验证代码并向我们提供建议。它清楚地说,空 catch 块规则 CC0004 已被违反。

案例研究

让我们来看另一种情况,看看分析器如何帮助提供有关代码修复或优化的建设性建议。以下代码实现了“Multiply”方法,其中有一些错误,以及该方法中一些未优化的代码。

   1: public int Multiply(int a, int b)
   2:  {
   3:      try
   4:      {
   5:          int times = 7;
   6:          int result = 0;
   7:          result = a * b;
   8:          return result;
   9:  
  10:      }
  11:  
  12:      catch { }
  13:  
  14:  
  15:  }

编写此代码时,我在“错误列表”窗口中收到以下消息、警告和错误。

请注意,“错误列表”窗口此处已配置为“当前文档”。您可以根据需要将其更改为“当前项目”或“所有打开的文档”。它显示 1 个错误、4 个警告和 1 条消息。错误由编译器显示,但警告和消息通过我们在项目中使用的分析器显示。警告是自我解释性的。如果您不知道如何修复它们,灯泡图标将帮助您修复它们。例如,对于 CC0105,请使用 var 而不是指定类型名称;如果您单击 int times=7;,将显示带有几个建议的灯泡图标,如下所示。

我们在这里看到,灯泡图标足够智能,可以处理分析器的建议,并提供建设性的解决方法来修复代码。第一个建议是使用“var”。在实际应用之前,您可以进行预览,您可以选择是在打开的文档、项目还是解决方案中进行此修复。灯泡图标提供的另一项建议是将变量设为常量。它之所以提出此建议,是因为它足够智能,可以分析出该变量在整个方法中都具有固定值。由于该变量在该方法中未被使用,因此它建议删除该未使用的变量,从而优化代码。

还有其他建议,例如“Suppress CC0105”、“CC0030”、“CS 0219”。灯泡图标为您提供了关于将来如何处理类似警告和错误的选项。如果您不希望为特定的分析器警告或消息显示建议,则可以选择抑制该消息,这意味着它将不再显示。

有两种方法可以执行抑制,代码建议为您提供了通过预览选择其中任何一种的选项。

1. **源代码中**。如上图所示,您可以抑制该特定代码行的警告/消息/错误。在这种情况下,将使用 #pragma 语句来封装底层代码,并且将不再向您显示该消息。代码将如下所示。

   1: public int Multiply(int a, int b)
   2:        {
   3:            try
   4:            {
   5: pragma warning disable CC0105 // You should use 'var' whenever possible.
   6:                int times = 7;
   7: pragma warning restore CC0105 // You should use 'var' whenever possible.
   8:                int result = 0;
   9:                result = a * b;
  10:                return result;
  11:  
  12:            }

2. **在抑制文件中**。另一种选择是将该抑制放在一个单独的文件中,这意味着抑制是项目级别的,并且您将不会在此特定项目的整个代码中看到该规则的消息/警告。此选项会创建一个单独的类文件并将其添加到您的项目中。

该文件名为 GlobalSuppressions.cs,并包含以下格式的被抑制消息。

   1:  
   2: // This file is used by Code Analysis to maintain SuppressMessage 
   3: // attributes that are applied to this project.
   4: // Project-level suppressions either have no target or are given 
   5: // a specific target and scoped to a namespace, type, member, etc.
   6:  
   7: [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "CC0105:You should use 'var' whenever possible.", Justification = "<Pending>", Scope = "member", Target = "~M:VS2015ConsoleApplication.Calculator.Multiply(System.Int32,System.Int32)~System.Int32")]
   8:

在这个特定的示例中,我选择不抑制消息,而是按照代码分析器的建议进行修复。让我们先修复 CC0105。

   1: public int Multiply(int a, int b)
   2:        {
   3:            try
   4:            {
   5:                var times = 7;
   6:                var result = 0;
   7:                result = a * b;
   8:                return result;
   9:  
  10:            }
  11:  
  12:            catch { }
  13:  
  14:  
  15:        }

因此,“错误列表”窗口如下所示。

我们在这里看到第一个警告已被处理,现在不再显示。现在修复 CS0219。

由于我们没有使用该变量,因此我将按照灯泡图标的建议删除未使用的变量。预览更改并将其应用于代码。这将从代码中删除 times 变量。现在,我们剩下以下代码。

   1: public int Multiply(int a, int b)
   2:        {
   3:            try
   4:            {
   5:                var result = 0;
   6:                result = a * b;
   7:                return result;
   8:  
   9:            }
  10:  
  11:            catch { }
  12:  
  13:  
  14:        }

以及以下“错误列表”,显示一个错误和一个警告。警告是关于我们正在使用的空 catch 块,即分析器规则列表中的 CC0004 标记。

同样,当您单击 catch 块代码旁显示的灯泡图标时,您将看到多个代码建议选项来修复此代码,如下所示。

您可以为您的空 catch 块代码选择各种选项,例如:1. 删除 try-catch 块。如果您与异常无关,或者可能意外应用了 try-catch 块,则可以选择此选项。如果您选择选项 2(删除空 catch 块并添加文档),此选项将删除 try-catch 块并在其中标记一个 TODO 项。如果您预览选项并应用修复,您将获得该方法的以下代码。

   1: public int Multiply(int a, int b)
   2:        {
   3:            {
   4:                var result = 0;
   5:                result = a * b;
   6:                return result;
   7:  
   8:            }
   9:            //TODO: Consider reading MSDN Documentation about how to use Try...Catch => http://msdn.microsoft.com/en-us/library/0yd65esw.aspx

该方法会放置一个注释掉的 TODO 项,说明阅读 MSDN 文档以了解如何使用 try-catch。

我们已经看到了如何抑制消息和警告,因此让我们尝试第三个选项,即“插入异常类以捕获”。请注意,除非您有自定义要求,否则您无需显式编写 catch 块。只需选择第三个选项并预览更改。

您可以看到该选项将插入带有 Exception 类对象作为参数的 catch 块的实现,并在 catch 块被调用时抛出异常。因此,代码如下所示。

   1: public int Multiply(int a, int b)
   2:        {
   3:            try
   4:            {
   5:                var result = 0;
   6:                result = a * b;
   7:                return result;
   8:  
   9:            }
  10:  
  11:            catch (Exception ex)
  12:            {
  13:                throw;
  14:            }

但是,一旦您进行此更改,您就会看到之前显示的错误已得到处理,因为现在每个方法退出点都有返回,但会引入一个新的警告,如下所示。

此图像显示我们正在尝试在 catch 块中声明一个未使用的变量。同样,您可以借助灯泡图标。

有两个选项:删除变量或抑制消息。或者,您也可以运用您的编码技巧,在代码块本身中对该变量做一些事情,例如显示错误消息、日志记录等。我将选择第一个选项,即删除未使用的变量。选择该选项后,变量将立即被删除,我们的代码将变得更清晰,如下所示,并且绝对没有错误、警告或消息。

   1: public int Multiply(int a, int b)
   2:  {
   3:      try
   4:      {
   5:          var result = 0;
   6:          result = a * b;
   7:          return result;
   8:      }
   9:      catch (Exception)
  10:      {
  11:          throw;
  12:      }
  13:  }

结论

在本文中,我们介绍了 Visual Studio 2015 的另一项出色功能:实时静态代码分析。我详细解释了一个案例研究,但您可以在玩此功能时探索其他规则和代码场景。我再次想提一下,您可以尝试此功能并探索它在代码分析和优化方面的能力,而且这一切都可以在您编写代码时即时完成,而无需编译代码。您可以尝试 Nuget 上提供的更多代码分析器。在下一篇文章中,我将介绍 Visual Studio 2015 提供的新增强的重命名和重构功能。

参考文献

https://app.pluralsight.com/courses/visual-studio-2015-first-look-ide

© . All rights reserved.