教程:在 Visual Studio 中扩展 C# 控制台应用和调试(第 2 部分(共 2 部分)

在本教程系列的第 2 部分中,你将更深入地了解日常开发所需的 Visual Studio 生成和调试功能。 这些功能包括管理多个项目、调试和引用第三方包。 运行在本教程 第 1 部分中创建的 C# 控制台应用,并探索 Visual Studio 集成开发环境(IDE)的某些功能。 本教程是两部分教程系列的第 2 部分。

在本教程中,你将完成以下任务:

  • 添加第二个项目。
  • 引用库并添加包。
  • 调试代码。
  • 检查已完成的代码。

先决条件

若要完成本文,可以使用以下任一计算器应用:

添加另一个项目

实际的代码涉及多个项目在一个方案中协同工作。 可以向你的计算器应用程序添加一个类库项目,该项目提供一些计算器功能。

在 Visual Studio 中,使用菜单命令 文件>添加>新建项目 添加新项目。 也可以右键单击“解决方案资源管理器”中的解决方案,从上下文菜单中添加项目

  1. 解决方案资源管理器中,右键单击解决方案节点,然后选择“添加>新建项目

  2. 在“添加新项目 窗口中,在”搜索“框中键入 类库。 选择 C# 类库 项目模板,然后选择 下一步

    类库项目模板选择的屏幕截图。

  3. 在“配置新项目”屏幕上,键入项目名称 CalculatorLibrary,然后选择 下一步

  4. 当系统询问时,请选择 .NET 3.1。 Visual Studio 将创建新项目并将其添加到解决方案。

    在解决方案资源管理器中添加的 CalculatorLibrary 类库项目的屏幕截图。

  5. Class1.cs 文件重命名为 CalculatorLibrary.cs。 若要重命名该文件,可以在 解决方案资源管理器 中右键单击名称,然后选择 重命名,选择名称,然后按 F2,或选择名称,然后再次选择键入。

    此时可能会出现一条消息,询问你是否要重命名文件中对 Class1 的引用。 答案并不重要,因为以后的步骤中将替换代码。

  6. 现在添加项目引用,以便第一个项目可以使用新类库公开的 API。 右键单击 计算器 项目中的 依赖项 节点,然后选择 添加项目引用

    “添加项目引用”菜单项的屏幕截图。

    此时将显示 引用管理器 对话框。 在此对话框中,可以添加对项目所需的其他项目、程序集和 COM DLL 的引用。

  7. 引用管理器 对话框中,选中 CalculatorLibrary 项目的复选框,然后选择 “确定”

    “引用管理器”对话框的屏幕截图。

    项目引用显示在解决方案资源管理器项目 节点下。

    解决方案资源管理器的屏幕截图,包括项目引用。

  8. 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;
             }
         }
     }
    
  9. 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 命名空间,但现在存在歧义。 CalculatorCalculatorLibrary中的类,还是 Calculator 是命名空间?

    若要解析歧义,请将命名空间从 Calculator 重命名为 Program.cs中的 CalculatorProgram

    // Program.cs
    namespace CalculatorProgram
    
  1. 解决方案资源管理器中,右键单击解决方案节点,然后选择“添加>新建项目

  2. 在“添加新项目 窗口中,在”搜索“框中键入 类库。 选择 C# 类库 项目模板,然后选择 下一步

    类库项目模板选择的屏幕截图。

  3. 在“配置新项目”屏幕中,键入项目名称 CalculatorLibrary,然后选择“下一步”

  4. 在“其他信息”屏幕上,“.NET 8.0”被选中。 选择 创建

    Visual Studio 将创建新项目并将其添加到解决方案。

    添加了 CalculatorLibrary 类库项目的解决方案资源管理器的屏幕截图。

  5. Class1.cs 文件重命名为 CalculatorLibrary.cs。 若要重命名该文件,可以在 解决方案资源管理器 中右键单击名称,然后选择 重命名,选择名称,然后按 F2,或选择名称,然后再次选择键入。

    一条消息可能会询问是否要重命名对文件中 Class1 的所有引用。 答案并不重要,因为以后的步骤中将替换代码。

  6. 现在添加项目引用,以便第一个项目可以使用新类库公开的 API。 右键单击 计算器 项目中的 依赖项 节点,然后选择 添加项目引用

    “添加项目引用”菜单项的屏幕截图。

    此时将显示 引用管理器 对话框。 在此对话框中,可以添加对项目所需的其他项目、程序集和 COM DLL 的引用。

  7. 引用管理器 对话框中,选中 CalculatorLibrary 项目的复选框,然后选择 “确定”

    “引用管理器”对话框的屏幕截图。

    项目引用在 解决方案资源管理器项目 节点下显示。

    解决方案资源管理器的屏幕截图,包含项目引用。

  8. 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;
             }
         }
     }
    
  9. 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

  1. 首先在 CalculatorLibrary.cs顶部添加 using 指令:

    // CalculatorLibrary.cs
    using System.IO;
    using System.Diagnostics;
    
  2. 使用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)
       {
    
  3. 将日志输出添加到每个计算。 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;
     }
    
  4. 回到 Program.cs,红色波浪下划线现在标记静态调用。 若要修复此错误,请在 while (!endApp) 循环前添加以下代码行来创建 calculator 变量:

    // Program.cs
    Calculator calculator = new Calculator();
    

    此外,修改 DoOperation 调用位置,使其以小写形式引用名为 calculator 的对象。 代码现在是成员调用,而不是对静态方法的调用。

    // Program.cs
    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. 再次运行应用。 完成后,右键单击 计算器 项目节点,然后选择 打开文件资源管理器中的文件夹

  6. 在文件资源管理器中,导航到 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

  1. 首先在 CalculatorLibrary.cs顶部添加 using 指令:

    // CalculatorLibrary.cs
    using System.Diagnostics;
    
  2. 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)
       {
    
  3. 将日志输出添加到每个计算。 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;
     }
    
  4. 回到 Program.cs,红色波浪下划线现在标记静态调用。 若要修复此错误,请在 while (!endApp) 循环前添加以下代码行来创建 calculator 变量:

    // Program.cs
    Calculator calculator = new Calculator();
    

    此外,将 DoOperation 的调用位置修改为以小写形式引用名为 calculator 的对象。 代码现在是成员调用,而不是对静态方法的调用。

    // Program.cs
    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. 再次运行应用。 完成后,右键单击 Calculator 项目节点,然后选择“在文件资源管理器中打开文件夹”

  6. 在文件资源管理器中,导航到 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 类库的主要分发方法。

  1. 解决方案资源管理器中,右键单击 CalculatorLibrary 项目的 依赖项 节点,然后选择 管理 NuGet 包

    快捷菜单上“管理 NuGet 包”的屏幕截图。

    快捷菜单上“管理 NuGet 包”的屏幕截图。

    NuGet 包管理器随即打开。

    NuGet 包管理器的屏幕截图。

  2. 搜索并选择 Newtonsoft.Json 包,然后选择 安装

    NuGet 包管理器中 Newtonsoft J SON NuGet 包信息的屏幕截图。

    Visual Studio 下载包并将其添加到项目中。 解决方案资源管理器的“引用”节点中会显示一个新条目。

    NuGet 包管理器中 Newtonsoft J SON NuGet 包信息的屏幕截图。

    如果系统提示你是否接受更改,请选择“确定”

    Visual Studio 下载包并将其添加到项目中。 解决方案资源管理器 节点中会出现一个新条目。

    在 CalculatorLibrary.cs 的开头为 Newtonsoft.Json 添加 using 指令

    // CalculatorLibrary.cs
    using Newtonsoft.Json;
    
  3. 创建 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();
         }
    
  4. 修改 DoOperation 方法以添加 JSON writer 代码:

         // 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;
         }
    
  5. 添加一个方法,在用户输入操作数据后完成 JSON 语法。

     // CalculatorLibrary.cs
     public void Finish()
     {
         writer.WriteEndArray();
         writer.WriteEndObject();
         writer.Close();
     }
    
  6. Program.cs结束时,在 return;之前,添加对 Finish的调用:

         // Program.cs
             // Add call to close the JSON writer before return
             calculator.Finish();
             return;
         }
    
  7. 生成并运行应用,完成输入几个操作后,通过输入 n 命令关闭应用。

  8. 在文件资源管理器中打开 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 调试器是一个功能强大的工具。 调试器可以单步执行你的代码以找到存在编程错误的确切位置。 然后,你可以了解需要进行哪些更正,并进行临时更改,以便可以继续运行应用。

  1. 在 Program.cs 中,单击以下代码行左侧的装订线。 还可以在行中单击并选择 F9,或右键单击该行,然后选择 断点>插入断点

    // Program.cs
    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    显示的红点表示断点。 可以使用断点暂停应用并检查代码。 可以在任何可执行的代码行上设置断点。

    显示设置断点的屏幕截图。

  2. 生成并运行应用。 输入以下计算值:

    • 对于第一个数字,请输入 8
    • 对于第二个数字,请输入 0
    • 对于运算符,我们来增添一些趣味。 输入 d

    应用在你创建断点的位置(由左侧的黄色指针和突出显示的代码表示)暂停。 突出显示的代码尚未执行。

    展示了如何命中断点的屏幕截图

    现在,应用暂停后,可以检查应用程序状态。

调试:查看变量

  1. 在突出显示的代码中,将鼠标悬停在变量上,如 cleanNum1op。 这些变量的当前值分别 8d 显示在数据提示中。

    显示查看数据提示的屏幕截图。

    调试时,检查变量是否保存预期的值通常对于修复问题至关重要。

  2. 在下面的窗格中,查看“局部变量”窗口。 如果关闭,请依次选择“调试”>“窗口”>“局部变量”将其打开

    局部变量 窗口显示当前处于作用域中的每个变量及其值和类型。

    “局部变量”窗口的屏幕截图。

    “局部变量”窗口的屏幕截图。

  3. 了解一下“自动变量”窗口。

    “自动变量”窗口类似于“局部变量”窗口,不同之处在于它显示应用暂停时所处的当前代码行前后紧跟的变量

    说明

    如果未看到“自动”窗口,请选择“调试”>“Windows”>“自动”将其打开。

接下来,你将在调试器中一次执行一个语句代码,这称为“单步执行”

调试:单步执行代码

  1. 按 F11,或选择“调试”>“单步执行”

    使用“单步执行”命令时,应用执行当前语句并前进到下一个可执行语句,通常是下一行代码。 左侧的黄色指针始终指示当前语句。

    展示了“单步执行”命令的屏幕截图 的屏幕截图

    你刚刚单步执行到了 Calculator 类中的 DoOperation 方法。

  2. 若要获取程序流的分层查看,请查看 调用堆栈 窗口。 如果关闭,请依次选择“调试”>“窗口”>“局部变量”将其打开

    调用堆栈屏幕截图

    此视图显示由黄色指针指示的当前 Calculator.DoOperation 方法。 第二行显示从 Program.cs中的 Main 方法调用该方法的函数。

    调用堆栈 窗口显示调用方法和函数的顺序。 此外,通过此窗口还可以访问许多调试器功能问,例如从其快捷菜单访问“转到源代码”

  3. 按 F10(或依次选择“调试”>“不进入子函数”)几次,直到应用在 switch 语句处暂停

    // CalculatorLibrary.cs
    switch (op)
    {
    

    “不进入子函数”命令与“单步执行”命令类似,不同之处在于,如果当前语句调用了函数,则调试器会运行函数中的代码,并且直到函数返回结果时才会暂停执行。 如果对特定函数不感兴趣,"Step Over" 比 "Step Into" 更快。

  4. 再按 F10 一次,使应用在以下代码行上暂停。

    // CalculatorLibrary.cs
    if (num2 != 0)
    {
    

    此代码检查是否存在除以 0 的情况。 如果应用继续,它将引发一般异常(错误),但你可能想要尝试其他操作,例如在控制台中查看实际返回的值。 一个选项是使用名为“编辑并继续”的调试器功能 对代码进行更改,然后继续调试。 但是,暂时修改执行流有不同的技巧。

调试:测试临时更改

  1. 选择当前在 if (num2 != 0) 语句处暂停的黄色指针,然后将它拖到下面的语句:

    // CalculatorLibrary.cs
    result = num1 / num2;
    

    在此处拖动指针会导致应用完全跳过 if 语句,以便你可以看到在除以零时会发生什么情况。

  2. F10 执行代码行。

  3. 如果将鼠标悬停在 result 变量上,则会显示 无穷大的值。 在 C# 中,Infinity 是除以零时的结果。

  4. F5,或选择 调试>继续调试

    无穷大符号在控制台中显示为数学运算的结果。

  5. 输入 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;
        }
    }
}

后续步骤

祝贺你完成本教程! 若要了解详细信息,请继续阅读以下内容: