练习 - 监视变量和执行流
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 部分显示当前的断点设置,可用于在调试会话期间启用或禁用特定断点。
配置应用程序和启动配置
在处理读取用户输入的控制台应用程序时,可能需要更新启动配置文件。
按如下所示更新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; }花点时间查看代码。
请注意以下内容:
- 该代码指定包含五个数字的整数数组。
- 代码在控制台中显示输出。
- 该代码提示用户输入一个起始元素号
n,并使用该元素号对数组元素从n到5进行求和。 - 代码在方法中计算总和,在控制台中显示结果,然后暂停。
注释
调试控制台面板不支持来自控制台的用户输入。
在 Visual Studio Code 的“文件”菜单上,选择“保存”。
在 “运行 ”菜单上,选择“ 删除所有断点”。
这将删除上一练习中剩余的任何断点。
在“运行和调试”视图中,选择“ 开始调试”。
请注意,执行代码行时
Console.Clear();发生错误。在“调试”工具栏上,选择“停止”。
切换到“资源管理器”视图,然后在编辑器中打开 launch.json 文件。
将
console属性的值更新如下:"console":"integratedTerminal",在 Visual Studio Code 文件 菜单上,选择“ 保存”,然后关闭 launch.json 文件。
查看应用程序输出并识别问题
查看应用程序的输出可能会显示编写代码时忽略的逻辑问题。
切换回“运行和调试”视图。
在“运行和调试”视图中,选择“ 开始调试”。
显示在“调试控制台”面板上的消息表明调试器正在连接到
Debug101.dll应用程序。请注意,不会显示任何错误消息。
在启动配置文件中,将
console属性的值从internalConsole更改为integratedTerminal,已经修复了控制台错误。 但现在需要找到包含输出的控制台。在编辑器下方的“面板”区域中,从“调试控制台”面板切换到“终端”面板。
请注意,代码执行已暂停,消息提示用户输入值
n。终端面板上的输出应如下所示:
The 'numbers' array contains: { 1 2 3 4 5 } To sum values 'n' through 5, enter a value for 'n':在 TERMINAL 命令提示符处,输入 3
查看应用程序的输出。
终端面板上的输出应如下所示:
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花一分钟时间考虑报告的值
sum以及控制台顶部显示的数组元素 3 到 5 的值。消息显示:
The sum of numbers 3 through 5 is: 9。 但是,数组元素 3 到 5 是3,4以及5。 报告的总和不应该是 12 吗?可以使用“运行和调试”视图的 VARIABLES 部分来调查问题。
监视变量状态
在某些情况下,只需监视变量状态就足以识别应用程序中的逻辑问题。
在以下代码行上设置断点:
Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex)}");在“运行和调试”视图中,选择“ 开始调试”。
从“调试控制台”面板切换到“终端”面板。
在 TERMINAL 命令提示符处,输入 3
代码执行将在断点处暂停。
花点时间查看“运行和调试”视图的 VARIABLES 部分。
请注意,
startIndex已为你输入的值赋值,即3。选择“单步执行”。
请注意,“变量”和“调用堆栈”部分已更新。
CALL STACK 部分显示代码执行已移动到
SumValues方法中。列出局部变量的 VARIABLES 节显示整数
n的值。 方法参数n从方法调用参数startIndex中为其值赋值。 在这种情况下,更改变量名称可以清楚地表明已传递值,而不是引用指针。注释
在这种情况下,可以在编辑器中看到大部分代码,因此可能不需要 CALL STACK 部分,但是当你使用深度嵌套和互连的方法调用处理较大的应用程序时,CALL STACK 节中显示的执行路径可能非常有用。
继续选择“单步执行”,直到分配给
sum的值不再为0。花点时间查看“变量”部分中显示的信息。
应会看到以下内容:
请注意,分配给
sum的值从0变为4。若要展开
numbers数组,请选择 数字 [int[]]。
回想一下,数组元素是使用从零开始的索引号访问的。
在这种情况下,逻辑错误是用户界面中的指令与基础代码之间的差异。 用户界面指的是数组项 1-5。 但是,代码使用用户输入的值来访问从零开始的数组元素。 数组索引为
3的元素存储了值4。 该代码不补偿从零开始的索引号。若要终止调试会话,请选择“ 停止”。
花点时间考虑如何解决此问题。
可以通过提示用户输入介于 0 到 4 之间的值,在用户界面中更正此问题。 还可以通过在输入的值中减去 1,在代码中对其进行更正。 一般来说,目标应该是一个清晰且易于遵循的用户界面。 在这种情况下,更新代码可能更好,如下所示:
Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex - 1)}");运行更新的代码将生成以下输出:
使用建议的方法更新代码,然后保存Program.cs文件。
清除断点,在调试器中重新运行应用程序,并验证预期结果是否显示在终端中。
你刚刚使用变量状态来识别和修复逻辑问题! 工作很好。
监视 watch 表达式
WATCH 节可用于监视基于一个或多个变量的表达式。
假设你正在处理对数据集执行数值计算的应用程序。 你认为当两个数值变量之间的比率大于 5 时,代码会生成不可靠的结果。 可以使用 WATCH 部分来监控计算后的比率。
使用以下代码更新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);保存Program.cs文件。
在最终代码行上设置断点。
设置以下 WATCH 表达式:
num2 / num1 > 5在“运行和调试”视图中,选择“ 开始调试”。
请注意“变量”和“监视”部分中显示的值。
选择 “继续 ”,直到看到 WATCH 表达式的计算结果为
true。如果 WATCH 表达式在第一次迭代中计算为
true,请选择“继续”几次,或直到第二次看到true。花点时间考虑 WATCH 部分的使用方式。
在此方案中,你确定当两个数值变量之间的比率大于 5 时,代码生成不可靠的结果。 您在 WATCH 节中构造了一个表示此条件的表达式。 现在可以使用 WATCH 部分来跟踪该条件。
修改在 VARIABLES 节中分配给变量的值
有时可能需要手动创建编程条件。 使用“运行和调试”视图的“变量”部分,可以通过在运行时更改变量的状态来执行此操作。
花点时间查看正在运行的代码。
请注意,代码永远不会退出
do循环,因为exit永远不会等于true。 这不是在实际应用程序中需要更改的编程条件,但它演示了该功能。在“变量”部分中,右键单击
exit [bool],然后选择“ 设置值”。通过 VARIABLES 节,可以在运行时更改分配给变量的值。 如果想要了解代码对特定条件的反应,这非常有用。
若要将
exit的值设置为true,请键入true然后按 Enter。在这种情况下,将
exit的值更改为true将导致执行while语句时应用程序关闭。选择 “继续”。
请注意,“调试控制台”面板会显示一条消息,告知程序已退出。
祝贺! 已成功使用“运行和调试”视图的 VARIABLES 和 WATCH 部分。
回顾
在本单元中,应谨记以下几个重要事项:
- 使用 RUN AND DEBUG 视图的 VARIABLES 部分监视变量状态。
- 使用“运行和调试”视图的“监视”部分跨时间或不同方法跟踪表达式。
- 使用 RUN AND DEBUG 视图的 CALL STACK 部分查找异常或 WATCH 表达式的源位置。
- 使用“变量”部分在运行时更改变量的分配值。