练习 - 监视变量和执行流

已完成

RUN AND DEBUG 视图为开发人员提供了一种简单的方法来监视变量和表达式、观察执行流,以及管理调试过程中的断点。

检查“运行和调试”视图的各部分

RUN AND DEBUG 视图的每个部分都提供独特的功能。 在调试过程中结合使用这些部分通常很有帮助。

“变量”部分

监视变量状态是代码调试的一个重要方面。 变量状态的意外更改通常有助于识别代码中的逻辑错误。

“VARIABLES”部分按范围组织变量。 该 Locals 作用域显示当前作用域(当前方法)中的变量。

注释

控制台应用程序的顶级语句部分被视为其自己的方法。 一个名为 Main 的方法。

可以通过选择范围名称左侧的箭头展开显示的范围。 还可以展开变量和对象。 以下屏幕截图显示了 numbers 作用域下展开的 Locals 数组。

显示“运行和调试”视图的“变量”部分中展开的变量的屏幕截图。

还可以在运行时使用“变量”部分更改变量的值。 可以双击变量名称,然后输入新值。

“监视”部分

如果要跨时间或不同的方法跟踪变量状态,该怎么办? 每次需要搜索变量时可能会很繁琐。 这就是 WATCH 区域派上用场的地方。

可以选择 “添加表达式 ”按钮(显示为加号: +)以输入变量名或要监视的表达式。 或者,可以右键单击 VARIABLES 部分中的变量并选择 Add to watch

当代码运行时,WATCH 节中的所有表达式都会自动更新。

“调用堆栈”部分

每次代码从另一种方法输入方法时,都会将调用层添加到应用程序的调用堆栈中。 当应用程序变得复杂且其他方法调用的方法列表很长时,调用堆栈表示方法调用的线索。

尝试查找异常或 WATCH 表达式的源位置时,CALL STACK 部分非常有用。 如果应用程序引发意外异常,通常会在控制台中看到如下所示的消息:

Exception has occurred: CLR/System.DivideByZeroException
An unhandled exception of type 'System.DivideByZeroException' occurred in Debug1.dll: 'Attempted to divide by zero.'
    at Program.<<Main>$>g__WriteMessage|0_1() in C:\Users\howdc\Desktop\Debug1\Program.cs:line 27
    at Program.<<Main>$>g__Process1|0_0() in C:\Users\howdc\Desktop\Debug1\Program.cs:line 16
    at Program.<Main>$(String[] args) in C:\Users\howdc\Desktop\Debug1\Program.cs:line 10

错误消息下缩进的一组 at Program ... 行称为堆栈跟踪。 堆栈跟踪列出了在异常前调用的每个方法的名称和源。 不过,这些信息可能有点难以解码,因为它还可以包含 .NET 运行时中的信息。 在此示例中,堆栈跟踪相当简洁,您可以看到异常发生在名为WriteMessage的方法中。 堆栈源自名为 Main的方法,这是控制台应用程序的顶级语句部分。

“调用堆栈”部分可帮助你避免难以破译掺杂 .NET 运行时信息的堆栈跟踪的情形。 默认情况下,它会筛选掉不需要的信息,以便仅显示你自己的代码中的相关方法。 可以手动展开调用堆栈,找出异常的来源。

“断点”部分

BREAKPOINTS 部分显示当前的断点设置,可用于在调试会话期间启用或禁用特定断点。

配置应用程序和启动配置

在处理读取用户输入的控制台应用程序时,可能需要更新启动配置文件。

  1. 按如下所示更新Program.cs文件中的代码:

    string? readResult;
    int startIndex = 0;
    bool goodEntry = false;
    
    int[] numbers = { 1, 2, 3, 4, 5 };
    
    // Display the array to the console.
    Console.Clear();
    Console.Write("\n\rThe 'numbers' array contains: { ");
    foreach (int number in numbers)
    {
        Console.Write($"{number} ");
    }
    
    // To calculate a sum of array elements, 
    //  prompt the user for the starting element number.
    Console.WriteLine($"}}\n\r\n\rTo sum values 'n' through 5, enter a value for 'n':");
    while (goodEntry == false)
    {
        readResult = Console.ReadLine();
        goodEntry = int.TryParse(readResult, out startIndex);
    
        if (startIndex > 5)
        {
            goodEntry = false;
            Console.WriteLine("\n\rEnter an integer value between 1 and 5");
        }
    }
    
    // Display the sum and then pause.
    Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex)}");
    
    Console.WriteLine("press Enter to exit");
    readResult = Console.ReadLine();
    
    // This method returns the sum of elements n through 5
    static int SumValues(int[] numbers, int n)
    {
        int sum = 0;
        for (int i = n; i < numbers.Length; i++)
        {
            sum += numbers[i];
        }
        return sum;
    }
    
  2. 花点时间查看代码。

    请注意以下内容:

    • 该代码指定包含五个数字的整数数组。
    • 代码在控制台中显示输出。
    • 该代码提示用户输入一个起始元素号 n,并使用该元素号对数组元素从 n5 进行求和。
    • 代码在方法中计算总和,在控制台中显示结果,然后暂停。

    注释

    调试控制台面板不支持来自控制台的用户输入。

  3. 在 Visual Studio Code 的“文件”菜单上,选择“保存”。

  4. “运行 ”菜单上,选择“ 删除所有断点”。

    这将删除上一练习中剩余的任何断点。

  5. 在“运行和调试”视图中,选择“ 开始调试”。

  6. 请注意,执行代码行时 Console.Clear(); 发生错误。

  7. 在“调试”工具栏上,选择“停止”

  8. 切换到“资源管理器”视图,然后在编辑器中打开 launch.json 文件。

  9. console属性的值更新如下:

    "console":"integratedTerminal",
    
  10. 在 Visual Studio Code 文件 菜单上,选择“ 保存”,然后关闭 launch.json 文件。

查看应用程序输出并识别问题

查看应用程序的输出可能会显示编写代码时忽略的逻辑问题。

  1. 切换回“运行和调试”视图。

  2. 在“运行和调试”视图中,选择“ 开始调试”。

    显示在“调试控制台”面板上的消息表明调试器正在连接到 Debug101.dll 应用程序。

  3. 请注意,不会显示任何错误消息。

    在启动配置文件中,将console属性的值从internalConsole更改为integratedTerminal,已经修复了控制台错误。 但现在需要找到包含输出的控制台。

  4. 在编辑器下方的“面板”区域中,从“调试控制台”面板切换到“终端”面板。

  5. 请注意,代码执行已暂停,消息提示用户输入值 n

    终端面板上的输出应如下所示:

    
    The 'numbers' array contains: { 1 2 3 4 5 }
    
    To sum values 'n' through 5, enter a value for 'n':
    
  6. 在 TERMINAL 命令提示符处,输入 3

  7. 查看应用程序的输出。

    终端面板上的输出应如下所示:

    
    The 'numbers' array contains: { 1 2 3 4 5 }
    
    To sum values 'n' through 5, enter a value for 'n':
    3
    
    The sum of numbers 3 through 5 is: 9
    press Enter to exit
    
  8. 花一分钟时间考虑报告的值 sum 以及控制台顶部显示的数组元素 3 到 5 的值。

    消息显示:The sum of numbers 3 through 5 is: 9。 但是,数组元素 3 到 5 是 34以及 5。 报告的总和不应该是 12 吗?

    可以使用“运行和调试”视图的 VARIABLES 部分来调查问题。

监视变量状态

在某些情况下,只需监视变量状态就足以识别应用程序中的逻辑问题。

  1. 在以下代码行上设置断点:

    Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex)}");
    
  2. 在“运行和调试”视图中,选择“ 开始调试”。

  3. 从“调试控制台”面板切换到“终端”面板。

  4. 在 TERMINAL 命令提示符处,输入 3

    代码执行将在断点处暂停。

  5. 花点时间查看“运行和调试”视图的 VARIABLES 部分。

    请注意, startIndex 已为你输入的值赋值,即 3

  6. 选择“单步执行”

  7. 请注意,“变量”和“调用堆栈”部分已更新。

    CALL STACK 部分显示代码执行已移动到 SumValues 方法中。

    列出局部变量的 VARIABLES 节显示整数 n的值。 方法参数 n 从方法调用参数 startIndex中为其值赋值。 在这种情况下,更改变量名称可以清楚地表明已传递值,而不是引用指针。

    注释

    在这种情况下,可以在编辑器中看到大部分代码,因此可能不需要 CALL STACK 部分,但是当你使用深度嵌套和互连的方法调用处理较大的应用程序时,CALL STACK 节中显示的执行路径可能非常有用。

  8. 继续选择“单步执行”,直到分配给 sum 的值不再为 0

  9. 花点时间查看“变量”部分中显示的信息。

    应会看到以下内容:

    显示“运行和调试”视图的“变量”部分的屏幕截图。

    请注意,分配给sum的值从0变为4

  10. 若要展开 numbers 数组,请选择 数字 [int[]]

    显示“运行和调试”视图的“变量”部分中展开的数组元素的屏幕截图。

  11. 回想一下,数组元素是使用从零开始的索引号访问的。

    在这种情况下,逻辑错误是用户界面中的指令与基础代码之间的差异。 用户界面指的是数组项 1-5。 但是,代码使用用户输入的值来访问从零开始的数组元素。 数组索引为3的元素存储了值4。 该代码不补偿从零开始的索引号。

  12. 若要终止调试会话,请选择“ 停止”。

  13. 花点时间考虑如何解决此问题。

    可以通过提示用户输入介于 0 到 4 之间的值,在用户界面中更正此问题。 还可以通过在输入的值中减去 1,在代码中对其进行更正。 一般来说,目标应该是一个清晰且易于遵循的用户界面。 在这种情况下,更新代码可能更好,如下所示:

    Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex - 1)}");
    

    运行更新的代码将生成以下输出:

    显示更新应用程序逻辑后的输出的屏幕截图。

  14. 使用建议的方法更新代码,然后保存Program.cs文件。

  15. 清除断点,在调试器中重新运行应用程序,并验证预期结果是否显示在终端中。

    你刚刚使用变量状态来识别和修复逻辑问题! 工作很好。

监视 watch 表达式

WATCH 节可用于监视基于一个或多个变量的表达式。

假设你正在处理对数据集执行数值计算的应用程序。 你认为当两个数值变量之间的比率大于 5 时,代码会生成不可靠的结果。 可以使用 WATCH 部分来监控计算后的比率。

  1. 使用以下代码更新Program.cs文件:

    bool exit = false;
    var rand = new Random();
    int num1 = 5;
    int num2 = 5;
    
    do
    {
        num1 = rand.Next(1, 11);
        num2 = num1 + rand.Next(1, 51);
    
    } while (exit == false);
    
  2. 保存Program.cs文件。

  3. 在最终代码行上设置断点。

  4. 设置以下 WATCH 表达式:

    num2 / num1 > 5
    
  5. 在“运行和调试”视图中,选择“ 开始调试”。

  6. 请注意“变量”和“监视”部分中显示的值。

  7. 选择 “继续 ”,直到看到 WATCH 表达式的计算结果为 true

    如果 WATCH 表达式在第一次迭代中计算为 true,请选择“继续”几次,或直到第二次看到 true

  8. 花点时间考虑 WATCH 部分的使用方式。

    在此方案中,你确定当两个数值变量之间的比率大于 5 时,代码生成不可靠的结果。 您在 WATCH 节中构造了一个表示此条件的表达式。 现在可以使用 WATCH 部分来跟踪该条件。

修改在 VARIABLES 节中分配给变量的值

有时可能需要手动创建编程条件。 使用“运行和调试”视图的“变量”部分,可以通过在运行时更改变量的状态来执行此操作。

  1. 花点时间查看正在运行的代码。

    请注意,代码永远不会退出 do 循环,因为 exit 永远不会等于 true。 这不是在实际应用程序中需要更改的编程条件,但它演示了该功能。

  2. 在“变量”部分中,右键单击 exit [bool],然后选择“ 设置值”。

    通过 VARIABLES 节,可以在运行时更改分配给变量的值。 如果想要了解代码对特定条件的反应,这非常有用。

  3. 若要将exit的值设置为true,请键入true然后按 Enter。

    在这种情况下,将exit的值更改为true将导致执行while语句时应用程序关闭。

  4. 选择 “继续”。

  5. 请注意,“调试控制台”面板会显示一条消息,告知程序已退出。

祝贺! 已成功使用“运行和调试”视图的 VARIABLES 和 WATCH 部分。

回顾

在本单元中,应谨记以下几个重要事项:

  • 使用 RUN AND DEBUG 视图的 VARIABLES 部分监视变量状态。
  • 使用“运行和调试”视图的“监视”部分跨时间或不同方法跟踪表达式。
  • 使用 RUN AND DEBUG 视图的 CALL STACK 部分查找异常或 WATCH 表达式的源位置。
  • 使用“变量”部分在运行时更改变量的分配值。