教程:了解如何使用 Visual Studio 调试 C# 代码
本文通过分步演练介绍了 Visual Studio 调试器的功能。 如果需要更加深入地了解调试器功能,请参阅初探调试器。 当你调试应用时,通常意味着运行附带有调试器的应用程序。 执行此任务时,调试器在运行过程中可提供许多方法让你查看代码的情况。 你可以逐步浏览代码、查看变量中存储的值、设置对变量的监视以查看值何时改变、检查代码的执行路径、查看代码分支是否正在运行等等。 如果这是你第一次在本练习中尝试调试代码,建议在浏览本文之前阅读零基础调试。
虽然演示应用为 C#,但大多数功能都适用于 C++、Visual Basic、F#、Python、JavaScript 和 Visual Studio 支持的其他语言(F# 不支持“编辑并继续”。F# 和 JavaScript 不支持“自动”窗口)。 屏幕截图为 C#。
在本教程中,你将:
- 启动调试器并命中断点。
- 了解在调试器中逐步执行代码的命令
- 检查数据提示和调试器窗口中的变量
- 检查调用堆栈
先决条件
须安装 Visual Studio 2022 且具有“.NET 桌面开发”工作负载。
必须安装 Visual Studio 2019 且具有“.NET Core 跨平台开发”工作负载 。
如果尚未安装 Visual Studio,请转到 Visual Studio 下载页免费安装。
如果需要安装工作负载但已有 Visual Studio,请转到“工具” >“获取工具和功能...” ,这会打开 Visual Studio 安装程序。 Visual Studio 安装程序启动。 选择“.NET Core 跨平台开发”工作负载,然后选择“修改” 。
如果你已有 Visual Studio 但未安装“.NET 桌面开发”工作负载,请转到“工具”>“获取工具和功能...”,随即会启动 Visual Studio 安装程序 。 在 Visual Studio 安装程序中,选择“.NET 桌面开发”工作负载,然后选择“修改” 。
创建项目
首先,创建一个 .NET Core 控制台应用程序项目。 项目类型随附了所需的全部模板文件,无需添加任何内容!
打开 Visual Studio。 如果“开始”窗口未打开,请选择“文件”>“启动窗口”。
在“开始”窗口中,选择“创建新项目”。
在“创建新项目”窗口的搜索框中输入“控制台”。 接下来,从“语言”列表中选择 C#,然后从“平台”列表中选择 Windows 。
应用语言和平台筛选器之后,对 .NET Core 选择“控制台应用”模板,然后选择“下一步”。
注意
如果未看到“控制台应用”模板,则可以通过“创建新项目”窗口安装该模板 。 在“找不到所需内容?”消息中,选择“安装更多工具和功能”链接 。 然后,在 Visual Studio 安装程序中,选择“.NET Core 跨平台开发”工作负载 。
在“配置新项目”窗口中,在“项目名称”框中输入“GetStartedDebugging”。 然后选择下一步。
选择推荐的目标框架(.NET 8.0 或“长期支持”),然后选择“创建”。
在“创建新项目”窗口的搜索框中输入“控制台”。 接下来,从“语言”列表中选择 C#,然后从“平台”列表中选择 Windows 。
应用语言和平台筛选器之后,选择“控制台应用”模板,然后选择“下一步”。
注意
如果未看到“控制台应用”模板,则可以通过“创建新项目”窗口安装该模板。 在“找不到所需内容?”消息中,选择“安装更多工具和功能”链接 。 然后在 Visual Studio 安装程序中,选择“.NET 桌面开发”工作负载。
在“配置新项目”窗口中,在“项目名称”框中输入“GetStartedDebugging”。 然后选择下一步。
在“其他信息”窗口中,确保在“框架”下拉菜单中选择“.NET 8.0”,然后选择“创建”。
此时,Visual Studio 将打开新项目。
创建应用程序
在 Program.cs 中,将所有默认代码替换为以下代码:
using System;
class ArrayExample
{
static void Main()
{
char[] letters = { 'f', 'r', 'e', 'd', ' ', 's', 'm', 'i', 't', 'h'};
string name = "";
int[] a = new int[10];
for (int i = 0; i < letters.Length; i++)
{
name += letters[i];
a[i] = i + 1;
SendMessage(name, a[i]);
}
Console.ReadKey();
}
static void SendMessage(string name, int msg)
{
Console.WriteLine("Hello, " + name + "! Count to " + msg);
}
}
启动调试器!
按 F5(“调试”>“开始调试”)或“调试”工具栏中的“开始调试”按钮 。
通过 F5 启动应用时,调试器会附加到应用进程,但现在我们还未执行任何特殊操作来检查代码。 因此应用就会加载,并且你会看到此控制台输出。
Hello, f! Count to 1 Hello, fr! Count to 2 Hello, fre! Count to 3 Hello, fred! Count to 4 Hello, fred ! Count to 5 Hello, fred s! Count to 6 Hello, fred sm! Count to 7 Hello, fred smi! Count to 8 Hello, fred smit! Count to 9 Hello, fred smith! Count to 10
在本教程中,你将使用调试器仔细查看此应用,并查看调试器功能。
按红色的停止按钮 (Shift + F5) 停止调试器。
在控制台窗口中,按某个键来关闭控制台窗口。
此处我们主要使用的是键盘快捷方式,因为这样可以快速执行调试器命令。 等效的命令(例如工具栏或菜单命令)也会注明。
若要启动调试器,请按 F5,或者在“标准”工具栏中选择“调试目标”按钮,或者在“调试”工具栏中选择“开始调试”按钮,或者从菜单栏中选择“调试”>“开始调试”。
按 F5 会启动应用,并且调试器会附加到应用进程。 由于我们尚未执行任何特殊操作来检查代码,因此应用将一直运行到完成为止,并且你会看到控制台输出。
Hello, f! Count to 1 Hello, fr! Count to 2 Hello, fre! Count to 3 Hello, fred! Count to 4 Hello, fred ! Count to 5 Hello, fred s! Count to 6 Hello, fred sm! Count to 7 Hello, fred smi! Count to 8 Hello, fred smit! Count to 9 Hello, fred smith! Count to 10
若要停止调试器,请按 Shift+F5,或者在“调试”工具栏中选择“停止调试”按钮,或者从菜单栏中选择“调试”>“停止调试”。
在控制台窗口中,按任意键关闭控制台窗口。
设置断点并启动调试器
在
Main
函数的for
循环中,通过单击以下代码行的左边距来设置断点:name += letters[i];
设置断点的位置会出现一个红圈 。
断点是可靠调试的最基本和最重要的功能之一。 断点指示 Visual Studio 应在哪个位置挂起你的运行代码,以使你可以查看变量的值或内存的行为,或确定代码的分支是否运行。
按 F5 或“开始调试”按钮。 应用随即启动,调试器将运行到你设置了断点的代码行。
黄色箭头表示调试器暂停时所在的语句,它还在同一点上暂停应用执行(此语句尚未执行)。
如果应用尚未运行,则按 F5 会启动调试器并在第一个断点处停止。 否则,按 F5 将继续运行应用至下一个断点 。
当你知道要详细检查的代码行或代码段时,断点功能非常有用。 有关可设置的不同类型断点(如条件断点)的信息,请参阅使用断点。
在
Main
函数的for
循环中,通过单击以下代码行的左边距来设置断点:name += letters[i];
设置断点的位置会出现一个红色圆圈。
断点是执行可靠调试所不可或缺的一项功能。 你可以设置断点,以便 Visual Studio 在你设置的断点处暂停正在运行的代码,这样你就可以查看变量值或内存行为,或者确定代码的分支是否已运行。
若要开始调试,请按 F5,或者在“标准”工具栏中选择“调试目标”按钮,或者在“调试”工具栏中选择“开始调试”按钮,或者从菜单栏中选择“调试”>“开始调试” 。 应用随即启动,调试器将运行到你设置了断点的代码行。
黄色箭头指向调试器暂停时所在的语句。 应用执行将在同一位置暂停,此处的语句尚未执行。
当应用未运行时,按 F5 会启动调试器。调试器将持续运行该应用,直至到达第一个断点。 如果应用在某个断点处暂停,则按 F5 会继续运行该应用,直至到达下一个断点。
如果你知道自己要详细检查的代码行或代码段,则断点功能非常有用。 有关可设置的各种断点(例如条件断点)的详细信息,请参阅使用断点。
使用数据提示浏览代码并检查数据
大多数情况下,我们使用键盘快捷方式,因为这是在调试器中快速执行应用的好方法(括号中显示了等效的命令,如菜单命令)。
当代码执行在
name += letters[i]
语句处暂停时,将鼠标悬停在letters
变量上,此时你会看到其默认值,即数组中第一个元素的值char[10]
。允许你检查变量的功能是调试器最有用的功能之一,并且有不同的方法来执行此操作。 通常,当尝试调试问题时,你试图找出变量是否存储了你期望它们在特定时间具有的值。
展开
letters
变量,查看其属性,其中包括变量包含的所有元素。接下来,将鼠标悬停在
name
变量上,会看到其当前值为空字符串。按两次 F10(或选择“调试”>“单步跳过”)前进到
SendMessage
方法调用,然后再按一次 F10。按 F10 将使调试器前进到下一条语句,但不会单步执行应用代码中的函数或方法(代码仍将执行)。 在进行
SendMessage
方法调用时,通过按 F10,我们跳过了SendMessage
的实现代码(我们现在可能对此不感兴趣)。多按几次 F10(或“调试”>“单步跳过”),通过
for
循环执行多次循环访问,再次在断点处暂停,每次都将鼠标悬停在name
变量上以检查其值 。变量的值随
for
循环的每次迭代而更改,显示的值依次为f
、fr
、fre
,依此类推。 要在此方案中更快地前进到循环,可以按 F5 (或选择“调试器”>“继续”),此操作会使你前进到断点,而不是下一条语句 。通常情况下,在调试时,需要快速检查变量的属性值,以查看它们是否存储了你希望它们存储的值,可根据数据提示执行此操作。
当代码执行在
Main
方法的for
循环中保持暂停状态时,按 F11(或选择“调试”>“单步执行”),直到在SendMessage
方法调用处暂停。你应该位于以下代码行:
SendMessage(name, a[i]);
再按一次 F11 单步执行到
SendMessage
方法 。黄色指针会前进到
SendMessage
方法。F11 是“单步执行”命令,每按一次,应用就执行下一个语句 。 F11 是一种以最详尽方式检查执行流的好方法。 默认情况下,调试器会跳过非用户代码(如果需要更多详细信息,请参阅仅我的代码)。
假设你已完成了对
SendMessage
方法的检查,并且希望退出该方法但保持位于调试器中。 可使用“单步跳出”命令执行此操作 。按 Shift + F11(或“调试”>“单步跳出”)。
此命令将恢复应用执行(并使调试器前进),直到当前方法或函数返回。
你应当回到
Main
方法的for
循环,在SendMessage
方法调用处暂停。 有关在代码中进行移动的不同方法的详细信息,请参阅浏览调试器中的代码。
当代码执行在
name += letters[i]
语句处暂停时,将鼠标悬停在letters
变量上以查看显示数组大小和元素类型char[10]
的数据提示。注意
调试器最有用的功能之一是能够检查变量。 通常,在尝试调试某个问题时,你会试图确定变量是否具有你期望它们在特定时间具有的值。 查看数据提示是进行这种检查的好办法。
展开
letters
变量以查看其所有数组元素以及这些元素的值。将鼠标悬停在
name
变量上以查看其当前值(一个空字符串)。若要使调试器前进到下一条语句,请按 F10,或者在“调试”工具栏中选择“单步跳过”按钮,或者从菜单栏中选择“调试”>“单步跳过” 。 再按 F10 两次以跳过
SendMessage
方法调用。按 F10 会使调试器前进,而不会单步执行函数或方法,不过,其代码仍会执行。 这样,就跳过了调试我们暂时不需要关注的
SendMessage
方法中的代码。若要迭代
for
循环几次,请反复按 F10。 在每次循环迭代期间,请在断点处暂停,然后将鼠标悬停在name
变量上以在数据提示中检查其值。变量的值随
for
循环的每次迭代而更改,显示的值依次为f
、fr
、fre
,依此类推。 若要使调试器在循环中更快前进,请按 F5,这样就会前进到断点而不是下一条语句。当代码执行在
Main
方法的for
循环中暂停时,按 F11,或者从“调试”工具栏中选择“单步执行”按钮,或者从菜单栏中选择“调试”>“单步执行”,直至到达SendMessage
方法调用。应在此代码行处暂停调试器:
SendMessage(name, a[i]);
若要单步执行
SendMessage
方法,请再次按 F11。黄色指针会前进到
SendMessage
方法。按 F11 可帮助你更深入地检查代码的执行流。 若要单步执行方法调用中的方法,请按 F11。 默认情况下,调试器将跳过非用户方法的单步执行。 若要了解如何调试非用户代码,请参阅仅我的代码。
调试完
SendMessage
方法后,可以返回到main
方法的for
循环。若要退出
SendMessage
方法,请按 Shift+F11,或者在“调试”工具栏中选择“单步跳出”按钮,或者从菜单栏中选择“调试”>“单步跳出”。“单步跳出”将恢复应用执行并使调试器前进,直到当前方法或函数返回。
你将在
Main
方法的for
循环中再次看到黄色指针,该指针暂停在SendMessage
方法调用处。 有关在代码中进行移动的不同方法的详细信息,请参阅浏览调试器中的代码。
使用“运行时单击”导航代码
按 F5 再次前进到断点。
在代码编辑器中向下滚动,并将鼠标悬停在
SendMessage
方法中的Console.WriteLine
方法上,直到出现绿色的“运行到单击处”按钮 。 按钮的工具提示显示“将执行运行到此处”。注意
“运行到单击处”按钮是 Visual Studio 2017 中的新增按钮。 (如果未看到绿色箭头按钮,请在此示例中改为使用 F11 以使调试器前进到正确的位置。 )
选择“运行到单击处”按钮。
调试器会前进到
Console.WriteLine
方法。使用此按钮类似于设置临时断点。 “运行到单击处”对于快速到达应用代码的可见区域十分方便(你可在任何打开的文件中选择)。
按 F5 再次前进到断点。
在代码编辑器中,将鼠标悬停在
SendMessage
方法中的Console.WriteLine
方法调用上,直到出现“运行到单击处”按钮。 按钮的工具提示显示“将执行运行到此处”。选择“运行到单击处”按钮。 或者,将光标置于
Console.WriteLine
语句上,然后按 Ctrl+F10。 或者,右键单击Console.WriteLine
方法调用,然后从上下文菜单中选择“运行到光标处”。调试器会前进到
Console.WriteLine
方法调用。使用“运行到单击处”按钮类似于设置临时断点,在已打开的文件的应用代码可见区域中,可以快速方便地使用这种方法。
快速重启应用
在调试工具栏中选择“重启”按钮 (Ctrl + Shift + F5)。
当你按下“重启”时,与停止应用并重启调试器相比,它节省了时间 。 调试器在执行代码命中的第一个断点处暂停。
调试器再次在你之前在 for
循环上设置的断点处停止。
若要在调试器中从头开始重新运行应用,请按 Ctrl+Shift+F5,或者在“调试”工具栏中选择“重启”按钮,或者从菜单栏中选择“调试”>“重启”。
“重启”将通过一个步骤停止调试器并重启调试器。 调试器重启时,将运行到第一个断点(先前在 for
循环中设置的断点),然后暂停。
使用“自动”和“局部变量”窗口检查变量
查看代码编辑器底部的“自动”窗口 。
如果已关闭,请依次选择“调试”>“Windows”>“自动”,在调试器中暂停时将其打开。
在“自动”窗口中,可看到变量及其当前值 。 “自动”窗口显示当前行或前一行使用的所有变量(检查文档中特定于语言的行为) 。
接下来,我们来看看“自动”窗口旁边的选项卡中的“局部变量”窗口 。
展开
letters
变量以显示其包含的元素。“局部变量”窗口显示当前作用域中的变量,即当前执行上下文 。
在调试时,“自动变量”和“局部变量”窗口会显示变量值 。 这两个窗口仅在调试会话期间才会显示。 “自动变量”窗口显示调试器所在的当前行和上一行中使用的变量。 “局部变量”窗口显示在局部范围内定义的变量,通常是当前函数或方法。
在调试器处于暂停状态时,查看代码编辑器底部的“自动变量”窗口。
如果“自动变量”窗口已关闭,请按下 Ctrl+D 和 A,或者从菜单栏中选择“调试”>“窗口”>“自动变量”。
在调试器仍处于暂停状态的情况下,在“自动变量”窗口旁边的选项卡中查看“局部变量”窗口 。
如果“局部变量”窗口已关闭,请按下 Ctrl+D 和 L,或者从菜单栏中选择“调试”>“窗口”>“局部变量”。
在“局部变量”窗口中,展开
letters
变量以查看其数组元素以及这些元素的值。
有关“自动变量”和“局部变量”窗口的详细信息,请参阅在“自动变量”和“局部变量”窗口中检查变量 。
设置监视
在主代码编辑器窗口中,右键单击 name
变量,然后选择“添加监视” 。
“监视”窗口将在代码编辑器的底部打开 。 可使用“监视”窗口指定要关注的变量(或表达式) 。
现在,你在 name
变量上设置好了监视,当你在调试器中移动时,可看到其值发生变化。 与其他变量窗口不同,“监视”窗口始终显示你正在监视的变量(当超出范围时,它们会变灰)。
可以指定你在逐步执行代码时想要观察的变量或表达式 - 将其添加到“监视”窗口即可。
当调试器处于暂停状态时,右键单击
name
变量并选择“添加监视”。“监视”窗口默认将在代码编辑器的底部打开。
对
name
变量设置监视后,接下来请逐步执行代码,以查看name
变量值在每次for
循环迭代中的变化。与其他变量窗口不同,“监视”窗口始终显示你正在监视的变量。 超出范围的变量显示为不可用。
有关“监视”窗口的详细信息,请参阅使用“监视”窗口监视变量。
检查调用堆栈
当代码执行在
for
循环中暂停时,选择“调用堆栈”窗口,该窗口默认会在右下窗格中打开。如果已关闭,请依次选择“调试”>“Windows”>“调用堆栈”,在调试器中暂停时将其打开。
按 F11 几次,直至看到调试器在
SendMessage
方法中暂停。 查看“调用堆栈”窗口 。“调用堆栈”窗口显示方法和函数被调用的顺序 。 最上面一行显示当前函数(此应用中的
SendMessage
方法)。 第二行显示SendMessage
是从Main
方法调用的,依此类推。注意
“调用堆栈”窗口类似于某些 IDE(如 Eclipse)中的调试透视图 。
调用堆栈是检查和理解应用执行流的好方法。
可以双击代码行查看相应的源代码,这还会更改调试器正在检查的当前范围。 此操作不会使调试器前进。
还可使用“调用堆栈”窗口中的右键单击菜单执行其他操作 。 例如,你可将断点插入到指定的函数中,使用“运行到光标处”推进调试器,然后检查源代码 。 有关详细信息,请参阅如何:检查调用堆栈。
“调用堆栈”显示方法和函数的调用顺序,可帮助你了解应用的执行流。
当调试器在
for
循环中处于暂停状态时,查看“调用堆栈”窗口,该窗口默认会在代码编辑器的右下窗格中打开。如果“调用堆栈”窗口已关闭,请按下 Ctrl+D 和 C,或者从菜单栏中选择“调试”>“窗口”>“调用堆栈”。
在“调用堆栈”窗口中,你将在当前
Main
方法处看到黄色指针。按 F11 几次,直至看到调试器在
SendMessage
方法中暂停。“调用堆栈”窗口的第一行显示当前函数,即
SendMessage
方法。 第二行显示从Main
方法调用的SendMessage
方法。注意
“调用堆栈”窗口类似于某些 IDE(例如 Eclipse)中的“调试”透视图。
在“调用堆栈”窗口中,可以双击代码行转到相应的源代码,这会更改调试器正在检查的当前范围。 此操作不会使调试器前进。
还可使用“调用堆栈”窗口中的右键单击菜单执行其他操作 。 例如,可将断点插入到指定的函数中、使用“运行到光标处”使调试器前进,或转到源代码。
有关“调用堆栈”的详细信息,请参阅如何:检查调用堆栈。
更改执行流
按两次 F11 以运行
Console.WriteLine
方法 。当调试器在
SendMessage
方法调用中暂停时,使用鼠标抓取黄色箭头或执行指针(在左边距中),然后在Console.WriteLine
语句中将指针向上拖动一行。按下 F11 。
调试器将重新运行
Console.WriteLine
方法(你会在控制台窗口输出中看到此操作)。通过更改执行流,你可以进行测试不同代码执行路径或重新运行代码等操作,而无需重启调试器。
警告
通常你需要小心使用此功能,工具提示中会出现警告。 你也可能会看到其他警告。 移动指针无法将应用程序还原到更早的应用状态。
按 F5 继续运行应用 。
恭喜你完成本教程!
在调试时,可以移动执行指针以更改应用流。
当调试器暂停于
for
循环中的SendMessage
方法调用处时,按 F11 三次以单步执行SendMessage
方法,并在执行该方法后跳过Console.WriteLine
方法。现在,调试器已暂停于
SendMessage
方法的最后一个右大括号处。使用鼠标抓取黄色箭头或执行指针(在左边缘中),然后将指针向上拖动一行。
调试器现在回到了
Console.WriteLine
语句。按 F11。
调试器重新运行
Console.WriteLine
方法,你将在控制台窗口输出中看到重复的行。按 F5 继续运行应用。
通过更改执行流,你可以进行测试不同代码执行路径或重新运行代码等操作,而无需重启调试器。
警告
请慎用此功能。 你将在执行指针的工具提示中看到一条警告,其中指出可能会出现意外的后果。 此外,还可能会看到其他警告。 移动执行指针无法将应用程序还原到以前的状态。
有关更改执行流的详细信息,请参阅移动指针以更改执行流。
恭喜你完成本教程!
后续步骤
在本教程中,你已了解了如何启动调试器、逐步执行代码以及检查变量。 你可能想要大致了解调试器功能并获取指向详细信息的链接。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈