使用 Visual Studio Code 进行调试

内容纲要

现在是时候实践你新获得的调试知识了。 这是你工作的第一天,现在可以通过修复公司旗舰产品(斐波那契计算器)中的 bug 来施展 .NET 调试技能了。

创建示例 .NET 项目以进行调试

若要设置 Visual Studio Code 以进行 .NET 调试,首先需要一个 .NET 项目。 Visual Studio Code 包含一个集成终端,这使创建新项目变得简单。

在 Visual Studio Code 中,选择“文件”>“打开文件夹”。

在选择的位置中创建名为 DotNetDebugging 的新文件夹。 然后选择“选择文件夹”。

从主菜单中选择“视图”>“终端”,以便从 Visual Studio Code 中打开集成终端。

在终端窗口中,复制粘贴以下命令:

dotnet new console

此命令会在文件夹中创建一个 Program.cs 文件(内附已编写的基本“Hello World”程序)。 它还将创建一个名为 DotNetDebugging.csproj 的 C# 项目文件。

在终端窗口中,复制粘贴以下命令来运行“Hello World”程序。

dotnet run

终端窗口显示“Hello World!”作为输出。

设置 Visual Studio Code 以进行 .NET 调试

选择 Program.cs 以打开它。

首次在 Visual Studio Code 中打开 C# 文件时,你将收到一条提示,提示你安装推荐的 C# 扩展。 如果看到此提示,请选择提示中的“安装”按钮。

Visual Studio Code 将安装 C# 扩展,并将显示另一条提示,提示你添加所需资产来生成和调试项目。 选择“是”按钮。

可以关闭“扩展:C#”选项卡,重点关注我们调试的代码。

添加斐波那契程序逻辑

我们的当前项目向控制台编写了“Hello World”消息,并没有太多内容需要调试。 相反,你将使用简短的 .NET 程序来计算第 N 号斐波那契数列。

斐波纳契数列是一组以数字 0 和 1 开头的数字,后面的每个数字都是前两个数字的和。 序列以此类推,如下所示:

0, 1, 1, 2, 3, 5, 8, 13, 21...

选择 Program.cs 以打开它。

将 Program.cs 的内容替换为以下代码:

int result = Fibonacci(5);
Console.WriteLine(result);

static int Fibonacci(int n)
{
    int n1 = 0;
    int n2 = 1;
    int sum;

    for (int i = 2; i < n; i++)
    {
        sum = n1 + n2;
        n1 = n2;
        n2 = sum;
    }

    return n == 0 ? n1 : n2;
}

备注

此代码包含错误,我们稍后将在本模块中进行调试。 在我们修复该错误之前,我们建议你不要在任何任务关键的斐波那契应用程序中使用它。

对于 Windows 和 Linux,通过选择“Ctrl+S”来保存该文件。 对于 Mac,请选择“Cmd+S”。

让我们来看看已更新的代码在调试之前是如何工作的。 通过在终端输入以下命令来运行程序:

dotnet run

终端输出中显示结果为 3。 斐波那契序列图显示了括号中每个值从零开始的序列位置,查阅该图时,你会看到结果应为 5。 现在可以熟悉调试器并修复此程序了。

0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...

分析问题

通过选择“运行”选项卡并选择“开始调试”按钮启动程序。

应看到程序快速完成。 这是正常的,因为尚未添加任何断点。

对于 Windows 和 Linux,如果调试控制台未出现,则选择“Ctrl+Shift+Y”。 对于 Mac,请选择“Cmd+Shift+Y”。 应看到几行诊断信息,后跟下面的几行内容:

...
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Threading.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Encoding.Extensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
3
The program '[88820] DotNetDebugging.dll' has exited with code 0 (0x0).

顶部的行指示默认调试设置启用“仅我的代码”选项。 这意味着,调试器将仅调试你的代码,除非禁用此模式,否则不会单步执行 .NET 的源代码。 使用此选项时,你便可以专注于调试代码。

在调试控制台输出的末尾,你会看到程序将 3 写入控制台,并存在代码 0。 通常,程序退出代码 0 表示程序已运行并退出且不崩溃。 但是,崩溃和返回正确的值之间存在差异。 在这种情况下,我们要求程序计算斐波那契数列的第 5 个值:

0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...

此列表中的第 5 个值为 5,但我们的程序返回了 3。 让我们使用调试器来诊断和解决此错误。

使用断点并逐步执行

通过单击 int result = Fibonacci(5); 上第 1 行的左边距来添加断点。

再次开始调试。 程序开始执行。 由于你设置了断点,因此程序会在第 1 行中断(暂停执行)。 使用调试器控件单步执行 Fibonacci() 函数。

检查变量状态

现在,使用“变量”面板检查不同变量的值。

  • 为 n 参数显示的值是什么?
  • 函数执行开始时,局部变量 n1n2 和 sum 的值分别是什么?

接下来,我们将使用“单步跳过”调试器控件前进到 for 循环。

在所读取的行上继续前进,直至到达 for 循环内的第一行:

sum = n1 + n2;

备注

你可能已注意到,需要在命令中执行多个步骤才能越过 for(...) {} 行。 出现这种情况的原因是此行上存在多个语句。 单步执行时,将移动到代码中的下一个语句。 通常,每行只有一个语句。 但如果不是这样,则需要执行多个步骤才能转到下一行。

思考代码

调试的一个重要部分是,停下来并对你认为代码的某些部分(函数和块,如循环)正在尝试执行的操作做出一些明智的猜想。 你不确定也没关系,这就是调试过程的一部分。 但主动参与调试过程会有助于更快地找到 bug。

在深入了解之前,让我们记住斐波纳契数列是一组以数字 0 和 1 开头的数字,后面的每个数字都是前两个数字的和。

这意味着:

Fibonacci(0) = 0
Fibonacci(1) = 1
Fibonacci(2) = 1 (0 + 1)
Fibonacci(3) = 2 (1 + 1)
Fibonacci(4) = 3 (1 + 2)
Fibonacci(5) = 5 (2 + 3)

了解该定义并查看此 for 循环,我们可以推断出:

  1. 该循环从 2 计数到 n(我们要查找的斐波纳契数列号)。
  2. 如果 n 小于 2,则循环将永不运行。 如果 n 为 0,则函数末尾的 return 语句将返回 0;如果 n 为 1 或 2,则返回 1。 根据定义,这是斐波纳契数列中的第 0 个、第 1 个和第 2 个值。
  3. 更有趣的情况是当 n 大于 2 时。 在这些情况下,当前值定义为前两个值的和。 因此,对于此循环,n1 和 n2 是前两个值,sum 是当前迭代的值。 因此,每次计算出前两个值的和并将其设置为 sum 时,我们都会更新 n1 和 n2 值。

好了,除此之外,我们不需要过多考虑。 我们可以依靠调试器完成一些操作。 但有必要考虑一下代码,看看它是否会执行预期的操作,如果未执行,则需要获取更多最新信息。

使用断点找到 bug

单步执行代码可能非常有用,但却很繁琐。 使用循环或其他重复调用的代码时尤为如此。 我们可以在循环的第一行上设置新断点,而不是反复单步执行循环。

当我们执行此操作时,使用策略放置断点非常重要。 我们对 sum 特别感兴趣,因为它表示当前最大斐波那契值。 因此,让我们在设置 sum 后在行上放置断点。

在第 13 行上添加第二个断点。

备注

如果你注意到继续运行代码,然后单步执行一行或两行代码,则可以轻松地将断点更新为更高效的行。

现在,在循环中设置好断点后,使用“继续”调试器控件前进,直至到达该断点。 查看本地变量,将看到以下几行内容:

n [int]: 5
n1 [int]: 0
n2 [int]: 1
sum [int]: 1
i [int]: 2

这些行看似都正确。 第一次单步执行循环时,前两个值的 sum 为 1。 我们可以利用断点,从而在下次单步执行循环时跳至该断点,而不是逐行单步执行。

选择“继续”以继续执行程序流,直至到达下一个断点,该断点将在下一次循环中出现。 

备注

使用“继续”时,无需担心跳过 bug。 你应该预料到,往往要多次调试代码才能找到问题。 相比非常小心谨慎地进行单步执行,多次运行代码往往速度更快。这一次,我们看到了以下值:

n [int]: 5
n1 [int]: 1
n2 [int]: 1
sum [int]: 2
i [int]: 3

让我们思考一下。 这些值是否仍有意义? 看起来像是有。 对于第三个斐波纳契数,我们预计将看到 sum 等于 2,实际上的确如此。

好了,让我们选择“继续”再次循环。

n [int]: 5
n1 [int]: 1
n2 [int]: 2
sum [int]: 3
i [int]: 4

同样,一切看起来都很好。 数列中的第 4 个值应为 3。

此时,你可能会想知道此代码是否一直是正确的,并且想象一下 bug! 让我们在循环中最后一次继续这样做。 再次选择“继续”。等待一分钟。 程序已完成运行并打印出 3! 这不正确。好了,不要担心。 我们并没有失败,而是了解了情况。 现在,我们知道代码会一直正确运行循环,直到 i 等于 4,但随后会在计算最终值之前退出。 我开始对 bug 的位置有所了解,你呢?

让我们在第 17 行上再设置一个断点,显示如下:

return n == 0 ? n1 : n2;

通过此断点,我们可以在函数退出前检查程序状态。 我们已了解到我们有望从第 1 行和第 13 行上的先前断点获得的所有内容,因此可以将其清除。

删除第 1 行和第 13 行上的先前断点。 为此,可以在行号旁边的空白处单击它们,或者在左下角的“断点”窗格中清除第 1 行和第 13 行的断点复选框。

现在,我们能够更好地了解发生的情况,并设置旨在捕获行为异常时的程序的断点,我们应该能够捕获此 bug!

最后一次启动调试器。

n [int]: 5
n1 [int]: 2
n2 [int]: 3
sum [int]: 3

这不是正确的。 我们特别要求提供 Fibonacci(5),而我们得到的是 Fibonacci(4)。 此函数返回 n2,每个循环迭代计算 sum 值并将 n2 设置为等于 sum。根据此信息以及以前的调试运行,我们可以看到,该循环在 i 为 4(而不是 5)时退出。让我们再仔细看看 for 循环的第一行。

for (int i = 2; i < n; i++)

好了,请等待一分钟! 这意味着,一旦 for 循环的顶部看到 i 不再小于 n,它将立即退出。 这表示,如果 i 等于 n,循环代码将无法运行。 我们想要的似乎是在 i <= n 之前运行,而不是:

for (int i = 2; i <= n; i++)

因此进行上述更改后,更新的程序应类似以下示例:

int result = Fibonacci(5);
Console.WriteLine(result);

static int Fibonacci(int n)
{
    int n1 = 0;
    int n2 = 1;
    int sum;

    for (int i = 2; i <= n; i++)
    {
        sum = n1 + n2;
        n1 = n2;
        n2 = sum;
    }

    return n == 0 ? n1 : n2;
}

停止调试会话(如果尚未这样做)。

接下来,对第 10 行进行前面的更改,并将断点留在第 17 行上。

重启调试程序。 这一次,当我们操作到第 17 行上的断点时,将看到以下值:

n [int]: 5
n1 [int]: 3
n2 [int]: 5
sum [int]: 5

喂! 看起来我们做到了! 很好,你已保挽救了 Fibonacci, Inc. 的一天!

选择“继续”,只是为了确保程序返回正确的值。

5
The program '[105260] DotNetDebugging.dll' has exited with code 0 (0x0).

这样会返回正确的输出。

你成功了! 你使用 Visual Studio Code 中的 .NET 调试器调试了你并未编写的某些代码。

在下一单元中,你将了解如何使用内置于 .NET 中的日志记录和跟踪功能,使编写的代码更易于调试。

给TA打赏
共{{data.count}}人
人已打赏
.NET开发工具

了解 Visual Studio Code 中的 .NET 调试器

2022-8-23 11:16:03

开发工具

使用 Visual Studio 将 Web 应用发布到 Azure

2022-8-25 11:05:32

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索