教程:在 Visual Studio 中扩展 C# 控制台应用和调试(第 2 部分(共 2 部分)
在本教程系列的第 2 部分中,你将更深入地了解日常开发所需的 Visual Studio 生成和调试功能。 这些功能包括管理多个项目、调试和引用第三方包。 运行在本教程 第 1 部分中创建的 C# 控制台应用,并探索 Visual Studio 集成开发环境(IDE)的某些功能。 本教程是两部分教程系列的第 2 部分。
在本教程中,你将完成以下任务:
- 添加第二个项目。
- 引用库并添加包。
- 调试代码。
- 检查已完成的代码。
先决条件
若要完成本文,可以使用以下任一计算器应用:
- 本教程系列第 1 部分中的计算器控制台应用。
- vs-tutorial-samples 存储库中的 C# 计算器应用。 若要开始,从存储库打开应用。
添加另一个项目
实际的代码涉及多个项目在一个方案中协同工作。 可以向你的计算器应用程序添加一个类库项目,该项目提供一些计算器功能。
在 Visual Studio 中,使用菜单命令 文件>添加>新建项目 添加新项目。 也可以右键单击“解决方案资源管理器”中的解决方案,从上下文菜单中添加项目。
在 解决方案资源管理器中,右键单击解决方案节点,然后选择“添加>新建项目。
在“添加新项目 窗口中,在”搜索“框中键入 类库。 选择 C# 类库 项目模板,然后选择 下一步。
在“配置新项目”屏幕上,键入项目名称 CalculatorLibrary,然后选择 下一步。
当系统询问时,请选择 .NET 3.1。 Visual Studio 将创建新项目并将其添加到解决方案。
将 Class1.cs 文件重命名为 CalculatorLibrary.cs。 若要重命名该文件,可以在 解决方案资源管理器 中右键单击名称,然后选择 重命名,选择名称,然后按 F2,或选择名称,然后再次选择键入。
此时可能会出现一条消息,询问你是否要重命名文件中对
Class1
的引用。 答案并不重要,因为以后的步骤中将替换代码。现在添加项目引用,以便第一个项目可以使用新类库公开的 API。 右键单击 计算器 项目中的 依赖项 节点,然后选择 添加项目引用。
此时将显示 引用管理器 对话框。 在此对话框中,可以添加对项目所需的其他项目、程序集和 COM DLL 的引用。
在 引用管理器 对话框中,选中 CalculatorLibrary 项目的复选框,然后选择 “确定”。
项目引用显示在解决方案资源管理器的项目 节点下。
在 Program.cs中,选择
Calculator
类及其所有代码,然后按 Ctrl+X 将其剪切。 然后,在 CalculatorLibrary.cs中,将代码粘贴到CalculatorLibrary
命名空间中。此外,在 Calculator 类之前添加
public
,以在库外公开它。CalculatorLibrary.cs 现在应类似于以下代码:
// CalculatorLibrary.cs using System; namespace CalculatorLibrary { public class Calculator { public static double DoOperation(double num1, double num2, string op) { double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. // Use a switch statement to do the math. switch (op) { case "a": result = num1 + num2; break; case "s": result = num1 - num2; break; case "m": result = num1 * num2; break; case "d": // Ask the user to enter a non-zero divisor. if (num2 != 0) { result = num1 / num2; } break; // Return text for an incorrect option entry. default: break; } return result; } } }
Program.cs 也有引用,但有错误指出
Calculator.DoOperation
调用无法解决。 错误是因为CalculatorLibrary
位于不同的命名空间中。 为了获得完全限定的引用,可以将CalculatorLibrary
命名空间添加到 Program.cs中的Calculator.DoOperation
调用中。// Program.cs result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
或者,可以尝试将
using
指令添加到 Program.cs 文件的开头:// Program.cs using CalculatorLibrary;
添加
using
指令应该允许从调用站点中删除CalculatorLibrary
命名空间,但现在存在歧义。Calculator
是CalculatorLibrary
中的类,还是Calculator
是命名空间?若要解析歧义,请将命名空间从
Calculator
重命名为 Program.cs中的CalculatorProgram
。// Program.cs namespace CalculatorProgram
在 解决方案资源管理器中,右键单击解决方案节点,然后选择“添加>新建项目。
在“添加新项目 窗口中,在”搜索“框中键入 类库。 选择 C# 类库 项目模板,然后选择 下一步。
在“配置新项目”屏幕中,键入项目名称 CalculatorLibrary,然后选择“下一步”。
在“其他信息”屏幕上,“.NET 8.0”被选中。 选择 创建。
Visual Studio 将创建新项目并将其添加到解决方案。
将 Class1.cs 文件重命名为 CalculatorLibrary.cs。 若要重命名该文件,可以在 解决方案资源管理器 中右键单击名称,然后选择 重命名,选择名称,然后按 F2,或选择名称,然后再次选择键入。
一条消息可能会询问是否要重命名对文件中
Class1
的所有引用。 答案并不重要,因为以后的步骤中将替换代码。现在添加项目引用,以便第一个项目可以使用新类库公开的 API。 右键单击 计算器 项目中的 依赖项 节点,然后选择 添加项目引用。
此时将显示 引用管理器 对话框。 在此对话框中,可以添加对项目所需的其他项目、程序集和 COM DLL 的引用。
在 引用管理器 对话框中,选中 CalculatorLibrary 项目的复选框,然后选择 “确定”。
项目引用在 解决方案资源管理器的 项目 节点下显示。
在 Program.cs中,选择
Calculator
类及其所有代码,然后按 Ctrl+X 将其剪切。 然后,在 CalculatorLibrary.cs中,将代码粘贴到CalculatorLibrary
命名空间中。此外,在 Calculator 类之前添加
public
,以在库外公开它。CalculatorLibrary.cs 现在应类似于以下代码:
// CalculatorLibrary.cs namespace CalculatorLibrary { public class Calculator { public static double DoOperation(double num1, double num2, string op) { double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. // Use a switch statement to do the math. switch (op) { case "a": result = num1 + num2; break; case "s": result = num1 - num2; break; case "m": result = num1 * num2; break; case "d": // Ask the user to enter a non-zero divisor. if (num2 != 0) { result = num1 / num2; } break; // Return text for an incorrect option entry. default: break; } return result; } } }
Program.cs 也有引用,但有错误指出
Calculator.DoOperation
调用无法解决。 错误是因为CalculatorLibrary
位于不同的命名空间中。 对于完全限定的引用,可以在 Program.cs中的Calculator.DoOperation
调用中添加CalculatorLibrary
命名空间:// Program.cs result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
或者,可以尝试将
using
指令添加到 Program.cs 文件的开头:// Program.cs using CalculatorLibrary;
添加
using
指令应允许从调用站点中删除CalculatorLibrary
命名空间。如果
Program.cs
代码位于Calculator
命名空间中,请将命名空间从Calculator
重命名为CalculatorProgram
以删除类名和命名空间名称之间的歧义。
引用 .NET 库:写入日志
可以使用 .NET Trace 类添加所有操作的日志,并将其写入文本文件。 Trace
类也可用于基本打印调试技术。 Trace
类位于 System.Diagnostics
中,并使用 System.IO
类,如 StreamWriter
。
首先在 CalculatorLibrary.cs顶部添加
using
指令:// CalculatorLibrary.cs using System.IO; using System.Diagnostics;
使用
Trace
类时,必须保留对类的引用,该引用与文件流相关联。 该要求意味着计算器作为对象效果更好,因此在 CalculatorLibrary.csCalculator
类的开头添加构造函数。此外,请删除
static
关键字,将静态DoOperation
方法更改为成员方法。// CalculatorLibrary.cs public Calculator() { StreamWriter logFile = File.CreateText("calculator.log"); Trace.Listeners.Add(new TextWriterTraceListener(logFile)); Trace.AutoFlush = true; Trace.WriteLine("Starting Calculator Log"); Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString())); } public double DoOperation(double num1, double num2, string op) {
将日志输出添加到每个计算。
DoOperation
现在应类似于以下代码:// CalculatorLibrary.cs public double DoOperation(double num1, double num2, string op) { double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. // Use a switch statement to do the math. switch (op) { case "a": result = num1 + num2; Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result)); break; case "s": result = num1 - num2; Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result)); break; case "m": result = num1 * num2; Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result)); break; case "d": // Ask the user to enter a non-zero divisor. if (num2 != 0) { result = num1 / num2; Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result)); } break; // Return text for an incorrect option entry. default: break; } return result; }
回到 Program.cs,红色波浪下划线现在标记静态调用。 若要修复此错误,请在
while (!endApp)
循环前添加以下代码行来创建calculator
变量:// Program.cs Calculator calculator = new Calculator();
此外,修改
DoOperation
调用位置,使其以小写形式引用名为calculator
的对象。 代码现在是成员调用,而不是对静态方法的调用。// Program.cs result = calculator.DoOperation(cleanNum1, cleanNum2, op);
再次运行应用。 完成后,右键单击 计算器 项目节点,然后选择 打开文件资源管理器中的文件夹。
在文件资源管理器中,导航到 bin/Debug/net8.0(或任何正在使用的 .NET 版本)下的输出文件夹,并打开 calculator.log 文件。 输出应如下所示:
Starting Calculator Log Started 7/9/2020 1:58:19 PM 1 + 2 = 3 3 * 3 = 9
此时,CalculatorLibrary.cs 应类似于以下代码:
// CalculatorLibrary.cs
using System;
using System.IO;
using System.Diagnostics;
namespace CalculatorLibrary
{
public class Calculator
{
public Calculator()
{
StreamWriter logFile = File.CreateText("calculator.log");
Trace.Listeners.Add(new TextWriterTraceListener(logFile));
Trace.AutoFlush = true;
Trace.WriteLine("Starting Calculator Log");
Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
}
public double DoOperation(double num1, double num2, string op)
{
double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
// Use a switch statement to do the math.
switch (op)
{
case "a":
result = num1 + num2;
Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
break;
case "s":
result = num1 - num2;
Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
break;
case "m":
result = num1 * num2;
Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
break;
case "d":
// Ask the user to enter a non-zero divisor.
if (num2 != 0)
{
result = num1 / num2;
Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
}
break;
// Return text for an incorrect option entry.
default:
break;
}
return result;
}
}
}
Program.cs 应类似于以下代码:
// Program.cs
using System;
using CalculatorLibrary;
namespace CalculatorProgram
{
class Program
{
static void Main(string[] args)
{
bool endApp = false;
// Display title as the C# console calculator app.
Console.WriteLine("Console Calculator in C#\r");
Console.WriteLine("------------------------\n");
Calculator calculator = new Calculator();
while (!endApp)
{
// Declare variables and set to empty.
string numInput1 = "";
string numInput2 = "";
double result = 0;
// Ask the user to type the first number.
Console.Write("Type a number, and then press Enter: ");
numInput1 = Console.ReadLine();
double cleanNum1 = 0;
while (!double.TryParse(numInput1, out cleanNum1))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput1 = Console.ReadLine();
}
// Ask the user to type the second number.
Console.Write("Type another number, and then press Enter: ");
numInput2 = Console.ReadLine();
double cleanNum2 = 0;
while (!double.TryParse(numInput2, out cleanNum2))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput2 = Console.ReadLine();
}
// Ask the user to choose an operator.
Console.WriteLine("Choose an operator from the following list:");
Console.WriteLine("\ta - Add");
Console.WriteLine("\ts - Subtract");
Console.WriteLine("\tm - Multiply");
Console.WriteLine("\td - Divide");
Console.Write("Your option? ");
string op = Console.ReadLine();
try
{
result = calculator.DoOperation(cleanNum1, cleanNum2, op);
if (double.IsNaN(result))
{
Console.WriteLine("This operation will result in a mathematical error.\n");
}
else Console.WriteLine("Your result: {0:0.##}\n", result);
}
catch (Exception e)
{
Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
}
Console.WriteLine("------------------------\n");
// Wait for the user to respond before closing.
Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
if (Console.ReadLine() == "n") endApp = true;
Console.WriteLine("\n"); // Friendly linespacing.
}
return;
}
}
}
可以使用 .NET Trace 类添加所有操作的日志,并将其写入文本文件。 Trace
类也可用于基本打印调试技术。 Trace
类位于 System.Diagnostics
中,并使用 System.IO
类,如 StreamWriter
。
首先在 CalculatorLibrary.cs顶部添加
using
指令:// CalculatorLibrary.cs using System.Diagnostics;
Trace
类的这种用法必须保留对该类的引用,该引用与文件流相关联。 该要求意味着计算器作为对象效果更好,因此在 CalculatorLibrary.csCalculator
类的开头添加构造函数。此外,请删除
static
关键字,将静态DoOperation
方法更改为成员方法。// CalculatorLibrary.cs public Calculator() { StreamWriter logFile = File.CreateText("calculator.log"); Trace.Listeners.Add(new TextWriterTraceListener(logFile)); Trace.AutoFlush = true; Trace.WriteLine("Starting Calculator Log"); Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString())); } public double DoOperation(double num1, double num2, string op) {
将日志输出添加到每个计算。
DoOperation
现在应类似于以下代码:// CalculatorLibrary.cs public double DoOperation(double num1, double num2, string op) { double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. // Use a switch statement to do the math. switch (op) { case "a": result = num1 + num2; Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result)); break; case "s": result = num1 - num2; Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result)); break; case "m": result = num1 * num2; Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result)); break; case "d": // Ask the user to enter a non-zero divisor. if (num2 != 0) { result = num1 / num2; Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result)); } break; // Return text for an incorrect option entry. default: break; } return result; }
回到 Program.cs,红色波浪下划线现在标记静态调用。 若要修复此错误,请在
while (!endApp)
循环前添加以下代码行来创建calculator
变量:// Program.cs Calculator calculator = new Calculator();
此外,将
DoOperation
的调用位置修改为以小写形式引用名为calculator
的对象。 代码现在是成员调用,而不是对静态方法的调用。// Program.cs result = calculator.DoOperation(cleanNum1, cleanNum2, op);
再次运行应用。 完成后,右键单击 Calculator 项目节点,然后选择“在文件资源管理器中打开文件夹”。
在文件资源管理器中,导航到 bin/Debug/下的输出文件夹,然后打开 calculator.log 文件。 输出应如下所示:
Starting Calculator Log Started 7/9/2020 1:58:19 PM 1 + 2 = 3 3 * 3 = 9
此时,CalculatorLibrary.cs 应类似于以下代码:
// CalculatorLibrary.cs
using System.Diagnostics;
namespace CalculatorLibrary
{
public class Calculator
{
public Calculator()
{
StreamWriter logFile = File.CreateText("calculator.log");
Trace.Listeners.Add(new TextWriterTraceListener(logFile));
Trace.AutoFlush = true;
Trace.WriteLine("Starting Calculator Log");
Trace.WriteLine(String.Format("Started {0}", System.DateTime.Now.ToString()));
}
public double DoOperation(double num1, double num2, string op)
{
double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
// Use a switch statement to do the math.
switch (op)
{
case "a":
result = num1 + num2;
Trace.WriteLine(String.Format("{0} + {1} = {2}", num1, num2, result));
break;
case "s":
result = num1 - num2;
Trace.WriteLine(String.Format("{0} - {1} = {2}", num1, num2, result));
break;
case "m":
result = num1 * num2;
Trace.WriteLine(String.Format("{0} * {1} = {2}", num1, num2, result));
break;
case "d":
// Ask the user to enter a non-zero divisor.
if (num2 != 0)
{
result = num1 / num2;
Trace.WriteLine(String.Format("{0} / {1} = {2}", num1, num2, result));
}
break;
// Return text for an incorrect option entry.
default:
break;
}
return result;
}
}
}
Program.cs 应类似于以下代码:
// Program.cs
using CalculatorLibrary;
namespace CalculatorProgram
{
class Program
{
static void Main(string[] args)
{
bool endApp = false;
// Display title as the C# console calculator app.
Console.WriteLine("Console Calculator in C#\r");
Console.WriteLine("------------------------\n");
Calculator calculator = new Calculator();
while (!endApp)
{
// Declare variables and set to empty.
// Use Nullable types (with ?) to match type of System.Console.ReadLine
string? numInput1 = "";
string? numInput2 = "";
double result = 0;
// Ask the user to type the first number.
Console.Write("Type a number, and then press Enter: ");
numInput1 = Console.ReadLine();
double cleanNum1 = 0;
while (!double.TryParse(numInput1, out cleanNum1))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput1 = Console.ReadLine();
}
// Ask the user to type the second number.
Console.Write("Type another number, and then press Enter: ");
numInput2 = Console.ReadLine();
double cleanNum2 = 0;
while (!double.TryParse(numInput2, out cleanNum2))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput2 = Console.ReadLine();
}
// Ask the user to choose an operator.
Console.WriteLine("Choose an operator from the following list:");
Console.WriteLine("\ta - Add");
Console.WriteLine("\ts - Subtract");
Console.WriteLine("\tm - Multiply");
Console.WriteLine("\td - Divide");
Console.Write("Your option? ");
string? op = Console.ReadLine();
// Validate input is not null, and matches the pattern
if (op == null || ! Regex.IsMatch(op, "[a|s|m|d]"))
{
Console.WriteLine("Error: Unrecognized input.");
}
else
{
try
{
result = calculator.DoOperation(cleanNum1, cleanNum2, op);
if (double.IsNaN(result))
{
Console.WriteLine("This operation will result in a mathematical error.\n");
}
else Console.WriteLine("Your result: {0:0.##}\n", result);
}
catch (Exception e)
{
Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
}
}
Console.WriteLine("------------------------\n");
// Wait for the user to respond before closing.
Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
if (Console.ReadLine() == "n") endApp = true;
Console.WriteLine("\n"); // Friendly linespacing.
}
return;
}
}
}
添加 NuGet 包:写入 JSON 文件
若要以 JSON 输出操作,这是用于存储对象数据的常用可移植格式,可以引用 Newtonsoft.Json NuGet 包。 NuGet 包是 .NET 类库的主要分发方法。
在 解决方案资源管理器中,右键单击 CalculatorLibrary 项目的 依赖项 节点,然后选择 管理 NuGet 包。
NuGet 包管理器随即打开。
搜索并选择 Newtonsoft.Json 包,然后选择 安装。
Visual Studio 下载包并将其添加到项目中。 解决方案资源管理器的“引用”节点中会显示一个新条目。
如果系统提示你是否接受更改,请选择“确定”。
Visual Studio 下载包并将其添加到项目中。 解决方案资源管理器的 包 节点中会出现一个新条目。
在 CalculatorLibrary.cs 的开头为
Newtonsoft.Json
添加using
指令。// CalculatorLibrary.cs using Newtonsoft.Json;
创建
JsonWriter
成员对象,并将Calculator
构造函数替换为以下代码:// CalculatorLibrary.cs JsonWriter writer; public Calculator() { StreamWriter logFile = File.CreateText("calculatorlog.json"); logFile.AutoFlush = true; writer = new JsonTextWriter(logFile); writer.Formatting = Formatting.Indented; writer.WriteStartObject(); writer.WritePropertyName("Operations"); writer.WriteStartArray(); }
修改
DoOperation
方法以添加 JSONwriter
代码:// CalculatorLibrary.cs public double DoOperation(double num1, double num2, string op) { double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. writer.WriteStartObject(); writer.WritePropertyName("Operand1"); writer.WriteValue(num1); writer.WritePropertyName("Operand2"); writer.WriteValue(num2); writer.WritePropertyName("Operation"); // Use a switch statement to do the math. switch (op) { case "a": result = num1 + num2; writer.WriteValue("Add"); break; case "s": result = num1 - num2; writer.WriteValue("Subtract"); break; case "m": result = num1 * num2; writer.WriteValue("Multiply"); break; case "d": // Ask the user to enter a non-zero divisor. if (num2 != 0) { result = num1 / num2; } writer.WriteValue("Divide"); break; // Return text for an incorrect option entry. default: break; } writer.WritePropertyName("Result"); writer.WriteValue(result); writer.WriteEndObject(); return result; }
添加一个方法,在用户输入操作数据后完成 JSON 语法。
// CalculatorLibrary.cs public void Finish() { writer.WriteEndArray(); writer.WriteEndObject(); writer.Close(); }
在 Program.cs结束时,在
return;
之前,添加对Finish
的调用:// Program.cs // Add call to close the JSON writer before return calculator.Finish(); return; }
生成并运行应用,完成输入几个操作后,通过输入 n 命令关闭应用。
在文件资源管理器中打开 calculatorlog.json 文件。 应会看到如下内容:
{ "Operations": [ { "Operand1": 2.0, "Operand2": 3.0, "Operation": "Add", "Result": 5.0 }, { "Operand1": 3.0, "Operand2": 4.0, "Operation": "Multiply", "Result": 12.0 } ] }
调试:设置并命中断点
Visual Studio 调试器是一个功能强大的工具。 调试器可以单步执行你的代码以找到存在编程错误的确切位置。 然后,你可以了解需要进行哪些更正,并进行临时更改,以便可以继续运行应用。
在 Program.cs 中,单击以下代码行左侧的装订线。 还可以在行中单击并选择 F9,或右键单击该行,然后选择 断点>插入断点。
// Program.cs result = calculator.DoOperation(cleanNum1, cleanNum2, op);
显示的红点表示断点。 可以使用断点暂停应用并检查代码。 可以在任何可执行的代码行上设置断点。
生成并运行应用。 输入以下计算值:
- 对于第一个数字,请输入 8。
- 对于第二个数字,请输入 0。
- 对于运算符,我们来增添一些趣味。 输入 d。
应用在你创建断点的位置(由左侧的黄色指针和突出显示的代码表示)暂停。 突出显示的代码尚未执行。
现在,应用暂停后,可以检查应用程序状态。
调试:查看变量
在突出显示的代码中,将鼠标悬停在变量上,如
cleanNum1
和op
。 这些变量的当前值分别8
和d
显示在数据提示中。调试时,检查变量是否保存预期的值通常对于修复问题至关重要。
在下面的窗格中,查看“局部变量”窗口。 如果关闭,请依次选择“调试”>“窗口”>“局部变量”将其打开。
局部变量 窗口显示当前处于作用域中的每个变量及其值和类型。
了解一下“自动变量”窗口。
“自动变量”窗口类似于“局部变量”窗口,不同之处在于它显示应用暂停时所处的当前代码行前后紧跟的变量。
说明
如果未看到“自动”窗口,请选择“调试”>“Windows”>“自动”将其打开。
接下来,你将在调试器中一次执行一个语句代码,这称为“单步执行”。
调试:单步执行代码
按 F11,或选择“调试”>“单步执行”。
使用“单步执行”命令时,应用执行当前语句并前进到下一个可执行语句,通常是下一行代码。 左侧的黄色指针始终指示当前语句。
的屏幕截图
你刚刚单步执行到了
Calculator
类中的DoOperation
方法。若要获取程序流的分层查看,请查看 调用堆栈 窗口。 如果关闭,请依次选择“调试”>“窗口”>“局部变量”将其打开。
调用堆栈屏幕截图
此视图显示由黄色指针指示的当前
Calculator.DoOperation
方法。 第二行显示从 Program.cs中的Main
方法调用该方法的函数。调用堆栈 窗口显示调用方法和函数的顺序。 此外,通过此窗口还可以访问许多调试器功能问,例如从其快捷菜单访问“转到源代码”。
按 F10(或依次选择“调试”>“不进入子函数”)几次,直到应用在
switch
语句处暂停。// CalculatorLibrary.cs switch (op) {
“不进入子函数”命令与“单步执行”命令类似,不同之处在于,如果当前语句调用了函数,则调试器会运行函数中的代码,并且直到函数返回结果时才会暂停执行。 如果对特定函数不感兴趣,"Step Over" 比 "Step Into" 更快。
再按 F10 一次,使应用在以下代码行上暂停。
// CalculatorLibrary.cs if (num2 != 0) {
此代码检查是否存在除以 0 的情况。 如果应用继续,它将引发一般异常(错误),但你可能想要尝试其他操作,例如在控制台中查看实际返回的值。 一个选项是使用名为“编辑并继续”的调试器功能 对代码进行更改,然后继续调试。 但是,暂时修改执行流有不同的技巧。
调试:测试临时更改
选择当前在
if (num2 != 0)
语句处暂停的黄色指针,然后将它拖到下面的语句:// CalculatorLibrary.cs result = num1 / num2;
在此处拖动指针会导致应用完全跳过
if
语句,以便你可以看到在除以零时会发生什么情况。按 F10 执行代码行。
如果将鼠标悬停在
result
变量上,则会显示 无穷大的值。 在 C# 中,Infinity 是除以零时的结果。按 F5,或选择 调试>继续调试。
无穷大符号在控制台中显示为数学运算的结果。
输入 n 命令来正确关闭应用。
代码完成
以下是完成所有步骤后 CalculatorLibrary.cs 文件的完整代码:
// CalculatorLibrary.cs
using System;
using System.IO;
using Newtonsoft.Json;
namespace CalculatorLibrary
{
public class Calculator
{
JsonWriter writer;
public Calculator()
{
StreamWriter logFile = File.CreateText("calculatorlog.json");
logFile.AutoFlush = true;
writer = new JsonTextWriter(logFile);
writer.Formatting = Formatting.Indented;
writer.WriteStartObject();
writer.WritePropertyName("Operations");
writer.WriteStartArray();
}
public double DoOperation(double num1, double num2, string op)
{
double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
writer.WriteStartObject();
writer.WritePropertyName("Operand1");
writer.WriteValue(num1);
writer.WritePropertyName("Operand2");
writer.WriteValue(num2);
writer.WritePropertyName("Operation");
// Use a switch statement to do the math.
switch (op)
{
case "a":
result = num1 + num2;
writer.WriteValue("Add");
break;
case "s":
result = num1 - num2;
writer.WriteValue("Subtract");
break;
case "m":
result = num1 * num2;
writer.WriteValue("Multiply");
break;
case "d":
// Ask the user to enter a non-zero divisor.
if (num2 != 0)
{
result = num1 / num2;
}
writer.WriteValue("Divide");
break;
// Return text for an incorrect option entry.
default:
break;
}
writer.WritePropertyName("Result");
writer.WriteValue(result);
writer.WriteEndObject();
return result;
}
public void Finish()
{
writer.WriteEndArray();
writer.WriteEndObject();
writer.Close();
}
}
}
下面是 Program.cs的代码:
// Program.cs
using System;
using CalculatorLibrary;
namespace CalculatorProgram
{
class Program
{
static void Main(string[] args)
{
bool endApp = false;
// Display title as the C# console calculator app.
Console.WriteLine("Console Calculator in C#\r");
Console.WriteLine("------------------------\n");
Calculator calculator = new Calculator();
while (!endApp)
{
// Declare variables and set to empty.
string numInput1 = "";
string numInput2 = "";
double result = 0;
// Ask the user to type the first number.
Console.Write("Type a number, and then press Enter: ");
numInput1 = Console.ReadLine();
double cleanNum1 = 0;
while (!double.TryParse(numInput1, out cleanNum1))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput1 = Console.ReadLine();
}
// Ask the user to type the second number.
Console.Write("Type another number, and then press Enter: ");
numInput2 = Console.ReadLine();
double cleanNum2 = 0;
while (!double.TryParse(numInput2, out cleanNum2))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput2 = Console.ReadLine();
}
// Ask the user to choose an operator.
Console.WriteLine("Choose an operator from the following list:");
Console.WriteLine("\ta - Add");
Console.WriteLine("\ts - Subtract");
Console.WriteLine("\tm - Multiply");
Console.WriteLine("\td - Divide");
Console.Write("Your option? ");
string op = Console.ReadLine();
try
{
result = calculator.DoOperation(cleanNum1, cleanNum2, op);
if (double.IsNaN(result))
{
Console.WriteLine("This operation will result in a mathematical error.\n");
}
else Console.WriteLine("Your result: {0:0.##}\n", result);
}
catch (Exception e)
{
Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
}
Console.WriteLine("------------------------\n");
// Wait for the user to respond before closing.
Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
if (Console.ReadLine() == "n") endApp = true;
Console.WriteLine("\n"); // Friendly linespacing.
}
calculator.Finish();
return;
}
}
}
以下是完成所有步骤后 CalculatorLibrary.cs 文件的完整代码:
// CalculatorLibrary.cs
using Newtonsoft.Json;
namespace CalculatorLibrary
{
public class Calculator
{
JsonWriter writer;
public Calculator()
{
StreamWriter logFile = File.CreateText("calculatorlog.json");
logFile.AutoFlush = true;
writer = new JsonTextWriter(logFile);
writer.Formatting = Formatting.Indented;
writer.WriteStartObject();
writer.WritePropertyName("Operations");
writer.WriteStartArray();
}
public double DoOperation(double num1, double num2, string op)
{
double result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error.
writer.WriteStartObject();
writer.WritePropertyName("Operand1");
writer.WriteValue(num1);
writer.WritePropertyName("Operand2");
writer.WriteValue(num2);
writer.WritePropertyName("Operation");
// Use a switch statement to do the math.
switch (op)
{
case "a":
result = num1 + num2;
writer.WriteValue("Add");
break;
case "s":
result = num1 - num2;
writer.WriteValue("Subtract");
break;
case "m":
result = num1 * num2;
writer.WriteValue("Multiply");
break;
case "d":
// Ask the user to enter a non-zero divisor.
if (num2 != 0)
{
result = num1 / num2;
}
writer.WriteValue("Divide");
break;
// Return text for an incorrect option entry.
default:
break;
}
writer.WritePropertyName("Result");
writer.WriteValue(result);
writer.WriteEndObject();
return result;
}
public void Finish()
{
writer.WriteEndArray();
writer.WriteEndObject();
writer.Close();
}
}
}
下面是 Program.cs的代码:
// Program.cs
using CalculatorLibrary;
namespace CalculatorProgram
{
class Program
{
static void Main(string[] args)
{
bool endApp = false;
// Display title as the C# console calculator app.
Console.WriteLine("Console Calculator in C#\r");
Console.WriteLine("------------------------\n");
Calculator calculator = new Calculator();
while (!endApp)
{
// Declare variables and set to empty.
// Use Nullable types (with ?) to match type of System.Console.ReadLine
string? numInput1 = "";
string? numInput2 = "";
double result = 0;
// Ask the user to type the first number.
Console.Write("Type a number, and then press Enter: ");
numInput1 = Console.ReadLine();
double cleanNum1 = 0;
while (!double.TryParse(numInput1, out cleanNum1))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput1 = Console.ReadLine();
}
// Ask the user to type the second number.
Console.Write("Type another number, and then press Enter: ");
numInput2 = Console.ReadLine();
double cleanNum2 = 0;
while (!double.TryParse(numInput2, out cleanNum2))
{
Console.Write("This is not valid input. Please enter an integer value: ");
numInput2 = Console.ReadLine();
}
// Ask the user to choose an operator.
Console.WriteLine("Choose an operator from the following list:");
Console.WriteLine("\ta - Add");
Console.WriteLine("\ts - Subtract");
Console.WriteLine("\tm - Multiply");
Console.WriteLine("\td - Divide");
Console.Write("Your option? ");
string? op = Console.ReadLine();
// Validate input is not null, and matches the pattern
if (op == null || ! Regex.IsMatch(op, "[a|s|m|d]"))
{
Console.WriteLine("Error: Unrecognized input.");
}
else
{
try
{
result = calculator.DoOperation(cleanNum1, cleanNum2, op);
if (double.IsNaN(result))
{
Console.WriteLine("This operation will result in a mathematical error.\n");
}
else Console.WriteLine("Your result: {0:0.##}\n", result);
}
catch (Exception e)
{
Console.WriteLine("Oh no! An exception occurred trying to do the math.\n - Details: " + e.Message);
}
}
Console.WriteLine("------------------------\n");
// Wait for the user to respond before closing.
Console.Write("Press 'n' and Enter to close the app, or press any other key and Enter to continue: ");
if (Console.ReadLine() == "n") endApp = true;
Console.WriteLine("\n"); // Friendly linespacing.
}
calculator.Finish();
return;
}
}
}
后续步骤
祝贺你完成本教程! 若要了解详细信息,请继续阅读以下内容: