Учебник. Расширение консольного приложения C# и отладка в Visual Studio (часть 2 из 2)

Во второй части серии мы подробно рассмотрим функции сборки и отладки в Visual Studio. Они понадобятся вам для повседневной разработки. К таким функциям относятся управление несколькими проектами, отладка и ссылка на сторонние пакеты. Вы запустите консольное приложение C#, созданное в части 1 этого учебника, и изучите некоторые функции интегрированной среды разработки (IDE) Visual Studio. Этот учебник представляет собой вторую часть серии, состоящей из двух частей.

В этом руководстве выполняются следующие задачи:

  • Добавите второй проект.
  • Ссылка на библиотеки и добавление пакетов
  • Выполните отладку своего кода.
  • Проверите готовый код.

Необходимые компоненты

Для работы с этой статьей можно использовать одно из следующих приложений калькулятора:

Добавление еще одного проекта

Реальный код связан с проектами, которые объединены в решение. Вы можете добавить проект библиотеки классов в приложение калькулятора, которое предоставляет некоторые функции калькулятора.

В Visual Studio выберите Файл>Добавить>Новый проект, чтобы добавить новый проект. В Обозревателе решений щелкните решение правой кнопкой мыши, чтобы добавить проект из контекстного меню.

  1. В обозревателе решений щелкните узел решения правой кнопкой мыши и выберите Добавить>Новый проект.

  2. В окне добавления нового проекта введите в поле поиска библиотека классов. Выберите шаблон проекта Библиотека классов на C# и нажмите Далее.

    Screenshot of Class Library project template selection.

  3. В диалоговом окне Настройка нового проекта введите имя проекта CalculatorLibrary и нажмите кнопку Далее.

  4. При появлении запроса выберите .NET 3.1. Visual Studio создаст новый проект и добавит его в решение.

    Screenshot of Solution Explorer with the CalculatorLibrary class library project added.

  5. Переименуйте файл Class1.cs в CalculatorLibrary.cs. Чтобы переименовать файл, щелкните правой кнопкой мыши имя в Обозреватель решений и выберите "Переименовать", выберите имя и нажмите клавишу F2 или выберите имя и еще раз введите его.

    В сообщении может появиться запрос о переименовании ссылок в Class1 в файле. Вы можете выбрать любой ответ, так как вы замените код на следующем шаге.

  6. Теперь необходимо добавить ссылку на проект, чтобы первый проект мог использовать API, предоставляемые новой библиотекой классов. Правой кнопкой мыши щелкните узел Зависимости в проекте Калькулятор и выберите Добавить ссылку на проект.

    Screenshot of the Add Project Reference menu item.

    Откроется диалоговое окно Диспетчер ссылок. Это диалоговое окно позволяет добавлять ссылки на другие проекты, а также сборки и библиотеки DLL COM, необходимые вашим проектам.

  7. В диалоговом окне Диспетчер ссылок установите флажок для проекта CalculatorLibrary и выберите OK.

    Screenshot of the Reference Manager dialog box.

    Ссылка на проект отображается в узле Проекты в обозревателе решений.

    Screenshot of Solution Explorer with project reference.

  8. В файле Program.cs выберите класс Calculator и весь его код и нажмите CTRL+X, чтобы вырезать его. Затем в CalculatorLibrary.cs вставьте код в пространство имен CalculatorLibrary.

    Добавьте public до класса калькулятора, чтобы предоставить его за пределами библиотеки.

    Теперь код в 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 в вызов Calculator.DoOperation.

    result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    Или попробуйте добавить директиву using в начало файла.

    using CalculatorLibrary;
    

    Добавление директивы using позволяет удалить пространство имен CalculatorLibrary из места вызова, но это приводит к неоднозначности. Является ли Calculator классом в CalculatorLibrary или Calculator — это пространство имен?

    Чтобы устранить неоднозначность, измените имя пространства имен Calculator на CalculatorProgram в файле Program.cs.

    namespace CalculatorProgram
    
  1. В обозревателе решений щелкните узел решения правой кнопкой мыши и выберите Добавить>Новый проект.

  2. В окне добавления нового проекта введите в поле поиска библиотека классов. Выберите шаблон проекта Библиотека классов на C# и нажмите Далее.

    Screenshot of Class Library project template selection.

  3. В диалоговом окне Настройка нового проекта введите имя проекта CalculatorLibrary и нажмите кнопку Далее.

  4. На экране "Дополнительные сведения" выбран .NET 8.0. Нажмите кнопку создания.

    Visual Studio создаст новый проект и добавит его в решение.

    Screenshot of Solution Explorer with the CalculatorLibrary class library project added.

  5. Переименуйте файл Class1.cs в CalculatorLibrary.cs. Чтобы переименовать файл, щелкните правой кнопкой мыши имя в Обозреватель решений и выберите "Переименовать", выберите имя и нажмите клавишу F2 или выберите имя и еще раз введите его.

    Сообщение может спросить, нужно ли переименовать все ссылки Class1 на файл. Вы можете выбрать любой ответ, так как вы замените код на следующем шаге.

  6. Теперь необходимо добавить ссылку на проект, чтобы первый проект мог использовать API, предоставляемые новой библиотекой классов. Правой кнопкой мыши щелкните узел Зависимости в проекте Калькулятор и выберите Добавить ссылку на проект.

    Screenshot of the Add Project Reference menu item.

    Откроется диалоговое окно Диспетчер ссылок. Это диалоговое окно позволяет добавлять ссылки на другие проекты, а также сборки и библиотеки DLL COM, необходимые вашим проектам.

  7. В диалоговом окне Диспетчер ссылок установите флажок для проекта CalculatorLibrary и выберите OK.

    Screenshot of the Reference Manager dialog box.

    Ссылка на проект отображается в узле Проекты в обозревателе решений.

    Screenshot of Solution Explorer with project reference.

  8. В файле Program.cs выберите класс Calculator и весь его код и нажмите CTRL+X, чтобы вырезать его. Затем в CalculatorLibrary.cs вставьте код в пространство имен CalculatorLibrary.

    Добавьте public до класса калькулятора, чтобы предоставить его за пределами библиотеки.

    Теперь код в 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 она находится в другом пространстве имен. Для полных ссылок можно добавить пространство имен CalculatorLibrary в вызов Calculator.DoOperation.

    result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
    

    Или попробуйте добавить директиву using в начало файла.

    using CalculatorLibrary;
    

    Добавление директивы using позволяет удалить пространство имен CalculatorLibrary из места вызова, но это приводит к неоднозначности. Является ли Calculator классом в CalculatorLibrary или Calculator — это пространство имен?

    Чтобы устранить неоднозначность, измените имя пространства имен Calculator на CalculatorProgram в файле Program.cs.

    namespace CalculatorProgram
    

Ссылки на библиотеки .NET: запись в журнал

Вы можете использовать класс Trace .NET, чтобы добавить журнал всех операций и записать его в текстовый файл. Класс Trace также удобно использовать и для основных методов отладки вывода. Класс Trace находится в System.Diagnostics и использует классы System.IO, например StreamWriter.

  1. Начните с добавления директив using в начало файла CalculatorLibrary.cs.

    using System.IO;
    using System.Diagnostics;
    
  2. Это использование класса Trace должно подразумевать ссылку на класс, который он связывает с файловым потоком. Это требование означает, что калькулятор будет работать лучше в качестве объекта, поэтому добавьте конструктор в начало класса Calculator в CalculatorLibrary.cs.

    Также удалите ключевое слово static, чтобы изменить статический метод DoOperation на метод-член.

    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 будет иметь следующий вид.

    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 красная волнистая линия теперь подчеркивает статический вызов. Чтобы исправить ошибку, создайте переменную calculator, добавив непосредственно перед циклом while (!endApp) следующую строку.

    Calculator calculator = new Calculator();
    

    Также измените место вызова DoOperation, чтобы оно ссылалось на объект с именем calculator в нижнем регистре. Теперь код является вызовом члена, а не вызовом статического метода.

    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. Снова запустите приложение. Когда все будет готово, щелкните правой кнопкой мыши узел проекта Калькулятор и выберите Открыть папку в проводнике.

  6. В проводнике перейдите в папку выходных данных в разделе bin/Debug/ и откройте файл calculator.log. Результат должен выглядеть следующим образом.

    Starting Calculator Log
    Started 7/9/2020 1:58:19 PM
    1 + 2 = 3
    3 * 3 = 9
    

Сейчас файл 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 должен выглядеть как следующий фрагмент кода.

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;
        }
    }
}

Вы можете использовать класс Trace .NET, чтобы добавить журнал всех операций и записать его в текстовый файл. Класс Trace также удобно использовать и для основных методов отладки вывода. Класс Trace находится в System.Diagnostics и использует классы System.IO, например StreamWriter.

  1. Начните с добавления директив using в начало файла CalculatorLibrary.cs.

    using System.Diagnostics;
    
  2. Это использование класса Trace должно подразумевать ссылку на класс, который он связывает с файловым потоком. Это требование означает, что калькулятор будет работать лучше в качестве объекта, поэтому добавьте конструктор в начало класса Calculator в CalculatorLibrary.cs.

    Также удалите ключевое слово static, чтобы изменить статический метод DoOperation на метод-член.

    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 будет иметь следующий вид.

    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 красная волнистая линия теперь подчеркивает статический вызов. Чтобы исправить ошибку, создайте переменную calculator, добавив непосредственно перед циклом while (!endApp) следующую строку.

    Calculator calculator = new Calculator();
    

    Также измените место вызова DoOperation, чтобы оно ссылалось на объект с именем calculator в нижнем регистре. Теперь код является вызовом члена, а не вызовом статического метода.

    result = calculator.DoOperation(cleanNum1, cleanNum2, op);
    
  5. Снова запустите приложение. Когда все будет готово, щелкните правой кнопкой мыши узел проекта Калькулятор и выберите Открыть папку в проводнике.

  6. В проводнике перейдите в папку выходных данных в разделе bin/Debug/ и откройте файл calculator.log. Результат должен выглядеть следующим образом.

    Starting Calculator Log
    Started 7/9/2020 1:58:19 PM
    1 + 2 = 3
    3 * 3 = 9
    

Сейчас файл 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 должен выглядеть как следующий фрагмент кода.

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;
        }
    }
}

Добавление пакета NuGet: запись в JSON-файл

Для вывода операций в формате JSON, популярном и переносимом формате хранения данных объекта, вы можете сослаться на пакет NuGet Newtonsoft.Json. Пакеты NuGet являются основным средством распространения библиотек классов .NET.

  1. В Обозревателе решений щелкните правой кнопкой мыши узел Зависимости для проекта CalculatorLibrary и выберите Управление пакетами NuGet.

    Screenshot of Manage NuGet Packages on the shortcut menu.

    Screenshot of Manage NuGet Packages on the shortcut menu.

    Откроется диспетчер пакетов NuGet.

    Screenshot of the NuGet Package Manager.

  2. Найдите и выберите пакет Newtonsoft.Json и нажмите кнопку Установить.

    Screenshot of Newtonsoft J SON NuGet package information in the NuGet Package Manager.

    Visual Studio скачивает пакет и добавляет его в проект. Новая запись отображается в узле "Ссылки" в Обозревателе решений.

    Screenshot of Newtonsoft J SON NuGet package information in the NuGet Package Manager. Если вам будет предложено принять изменения, нажмите кнопку "ОК".

    Visual Studio скачивает пакет и добавляет его в проект. Новая запись отображается в узле Пакеты в Обозревателе решений.

    Добавьте директиву using для Newtonsoft.Json в начало файла CalculatorLibrary.cs.

    using Newtonsoft.Json;
    
  3. Создайте объект члена JsonWriter и замените конструктор 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();
         }
    
  4. Измените метод DoOperation, чтобы добавить код JSON writer.

         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 после ввода пользователем всех данных для операции.

     public void Finish()
     {
         writer.WriteEndArray();
         writer.WriteEndObject();
         writer.Close();
     }
    
  6. В конце файла Program.cs перед return; добавьте вызов к Finish.

             // 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 или щелкнуть правой кнопкой мыши и выбрать Точка останова>Вставить точку останова.

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

    Красная точка указывает на точку останова. Для приостановки приложения и проверки кода можно использовать точки останова. Вы можете установить точку останова в любой исполняемой строке кода.

    Screenshot that shows setting a breakpoint.

  2. Выполните сборку и запустите приложение. Введите следующие значения для вычисления.

    • Для первого числа введите 8.
    • Для второго числа введите 0.
    • Давайте поэкспериментируем с оператором. Введите d.

    Приложение приостанавливается в созданной точке останова, которая обозначается желтым указателем слева и выделенным кодом. Выделенный код еще не выполнен.

    Screenshot of hitting a breakpoint

    Теперь, когда приложение приостановлено, вы можете проверить его состояние.

Отладка. Просмотр переменных

  1. В выделенном коде наведите указатель мыши на переменные, такие как cleanNum1 и op. Текущие значения этих переменных, 8 и d соответственно, отображаются в подсказках по данным.

    Screenshot that shows viewing a DataTip.

    При отладке очень важно выполнить проверку переменных на соответствующие значения для устранения проблем.

  2. В нижней области просмотрите окно Локальные. Если оно закрыто, выберите Отладка>Окна>Локальные, чтобы открыть его.

    В окне Локальные отображаются все переменные, находящиеся в области, а также их значения и типы.

    Screenshot of the Locals window.

    Screenshot of the Locals window.

  3. Взгляните на окно Видимые.

    Окно "Видимые" аналогично окну Локальные, но отображает переменную непосредственно перед текущей строкой кода и после строки, в которой приостановлено приложение.

    Примечание.

    Если окно "Авто" не отображается, выберите "Отладка>>windows Autos", чтобы открыть его.

Далее можно поочередно выполнить код в отладчике по одной инструкции за раз. Это называется пошаговым выполнением.

Отладка. Пошаговое прохождение кода

  1. Нажмите клавишу F11 или выберите Отладка>Выполнять по шагам.

    С помощью команды "Выполнять по шагам" приложение выполняет текущий оператор и переходит к следующему исполняемому оператору. Как правило, это следующая строка кода. Желтый указатель слева всегда указывает на текущий оператор.

    Screenshot of step into command

    Вы только что выполнили по шагам метод DoOperation в классе Calculator.

  2. Чтобы получить иерархический обзор выполнения программы, просмотрите окно Стек вызовов. Если оно закрыто, выберите Отладка>Окна>Стек вызовов, чтобы открыть его.

    Screenshot of the call stack

    В этом представлении показан текущий метод Calculator.DoOperation, обозначенный желтым указателем. Во второй строке показана функция, которая вызвала метод из метода Main в Program.cs.

    В окне Стек вызовов показан порядок вызова методов и функций. Это окно предоставляет доступ ко многим функциям отладчика, таким как Перейти к исходному коду, из контекстного меню.

  3. Нажмите клавишу F10 или выберите Отладка>Шаг с обходом несколько раз, пока приложение не остановится в операторе switch.

    switch (op)
    {
    

    Команда "Шаг с обходом" аналогична команде "Выполнять по шагам", за исключением того, что если текущий оператор вызывает функцию, то отладчик выполняет код в вызываемой функции и не приостанавливает выполнение до возврата функции. Команда "Шаг с обходом" выполняется быстрее, чем "Выполнять по шагам", если вас не интересует определенная функция.

  4. Нажмите клавишу F10 еще раз, чтобы приложение приостанавливалось на следующей строке кода.

    if (num2 != 0)
    {
    

    Этот код проверяет деление на нуль. Если приложение продолжается, он создает общее исключение (ошибка), но может потребоваться попробовать что-то другое, например просмотр фактического возвращаемого значения в консоли. Один из вариантов — использовать функцию отладчика Изменить и продолжить, чтобы внести изменения в код и продолжить отладку. Тем не менее мы покажем другой метод для временного изменения потока выполнения.

Отладка. Тестирование временного изменения

  1. Выберите желтый указатель, который в данный момент приостановлен на операторе if (num2 != 0), и перетащите его в следующий оператор.

    result = num1 / num2;
    

    Если перевести указатель сюда, приложение полностью пропускает оператор if, чтобы вы могли увидеть, что происходит при делении на ноль.

  2. Нажмите клавишу F10, чтобы выполнить строку кода.

  3. При наведении указателя мыши на переменную result отображается значение бесконечности. В C# бесконечность является результатом деления на ноль.

  4. Нажмите клавишу F5 или выберите Отладка>Продолжить отладку.

    Символ бесконечности отображается в консоли как результат математической операции.

  5. Закройте приложение должным образом с помощью команды n.

Полный код

Ниже приведен полный код для файла 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:

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 после выполнения всех шагов.

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:

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;
        }
    }
}

Следующие шаги

Поздравляем с завершением этого учебника! Для получения дополнительных сведений изучите следующие ресурсы.