チュートリアル:C# コンソール アプリを拡張し、Visual Studio でデバッグする (パート 2/2)
このチュートリアル シリーズのパート 2 では、日々の開発で必要な Visual Studio のビルドおよびデバッグ機能について少し詳しく説明します。 これらの機能には、複数のプロジェクトの管理、デバッグ、サードパーティ パッケージの参照が含まれます。 このチュートリアルのパート 1 で作成した C# コンソール アプリを実行し、Visual Studio 統合開発環境 (IDE) の機能をいくつか確認します。 このチュートリアルは、2 部構成のチュートリアル シリーズのパート 2 です。
このチュートリアルでは、次のタスクを実行します。
- 2 つ目のプロジェクトを追加する。
- ライブラリを参照し、パッケージを追加します。
- コードをデバッグします。
- 完成したコードを調べる。
前提条件
この記事を読み進める際に、これらの電卓アプリのいずれかを使用できます。
- このチュートリアルのパート 1 の電卓コンソール アプリ。
- vs-tutorial-samples リポジトリの C# 電卓アプリ。 開始するには、リポジトリからアプリを開きます。
別のプロジェクトを追加する
実際のコードでは、ソリューション内でプロジェクトを連携させる必要があります。 いくつかの電卓関数を提供するクラス ライブラリ プロジェクトを電卓アプリに追加できます。
Visual Studio で、メニュー コマンドの [ファイル]>[追加]>[新しいプロジェクト] を使用して、新しいプロジェクトを追加します。 ソリューション エクスプローラーでソリューションを右クリックして、コンテキスト メニューからプロジェクトを追加することもできます。
ソリューション エクスプローラーで、ソリューション ノードを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。
[新しいプロジェクトの追加] ウィンドウで、検索ボックスに「クラス ライブラリ」と入力します。 C# クラス ライブラリ プロジェクト テンプレートを選んでから、 [次へ] を選択します。
[新しいプロジェクトの構成] 画面で、プロジェクト名として「CalculatorLibrary」と入力してから [次へ] を選択します。
プロンプトが表示されたら .NET 3.1 を選択します。 Visual Studio によって、新しいプロジェクトが作成され、ソリューションに追加されます。
Class1.cs ファイルの名前を CalculatorLibrary.cs に変更します。 ファイルの名前を変更するには、ソリューション エクスプローラーで名前を右クリックして、[名前の変更] を選択し、名前を選んで F2 キーを押します。または、名前を選んでもう一度選択して入力します。
ファイル内の
Class1
への参照の名前を変更するかどうかを確認するメッセージが表示される場合があります。 後の手順でコードを置き換えるので、どのように答えてもかまいません。ここで、新しいクラス ライブラリによって公開される API を最初のプロジェクトで使用できるように、プロジェクト参照を追加します。 [Calculator] プロジェクト内の [依存関係] ノードを右クリックし、 [プロジェクト参照の追加] を選択します。
[参照マネージャー] ダイアログ ボックスが表示されます。 このダイアログ ボックスで、他のプロジェクトへの参照、自分のプロジェクトに必要なアセンブリと COM DLL を追加できます。
[参照マネージャー] ダイアログ ボックスで、 [CalculatorLibrary] プロジェクトのチェックボックスをオンにしてから、 [OK] を選択します。
プロジェクト参照は、ソリューション エクスプローラーで [プロジェクト] ノードの下に表示されます。
Program.cs で、
Calculator
クラスとそのすべてのコードを選択し、CTRL+X キーを押してそれを切り取ります。 その後、CalculatorLibrary.cs で、CalculatorLibrary
名前空間にコードを貼り付けます。また、Calculator クラスの前に
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; } } }
Program.cs にも参照がありますが、
Calculator.DoOperation
呼び出しが解決されないというエラーが表示されます。 このエラーは、CalculatorLibrary
が別の名前空間にあるためです。 完全修飾参照の場合は、CalculatorLibrary
名前空間をCalculator.DoOperation
呼び出しに追加できます。result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
using
ディレクティブをファイルの先頭に追加してみることもできます。using CalculatorLibrary;
using
ディレクティブを追加すると、呼び出しサイトからCalculatorLibrary
名前空間を削除できるようになるはずですが、今度はあいまいさが生じます。Calculator
はCalculatorLibrary
内のクラスですか、それともCalculator
は名前空間ですか?あいまいさを解決するには、Program.cs の名前空間の名前を
Calculator
からCalculatorProgram
に変更します。namespace CalculatorProgram
ソリューション エクスプローラーで、ソリューション ノードを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。
[新しいプロジェクトの追加] ウィンドウで、検索ボックスに「クラス ライブラリ」と入力します。 C# クラス ライブラリ プロジェクト テンプレートを選んでから、 [次へ] を選択します。
[新しいプロジェクトの構成] 画面で、プロジェクト名として「CalculatorLibrary」と入力してから [次へ] を選択します。
[追加情報] 画面で、.NET 8.0 が選択されています。 [作成] を選択します
Visual Studio によって、新しいプロジェクトが作成され、ソリューションに追加されます。
Class1.cs ファイルの名前を CalculatorLibrary.cs に変更します。 ファイルの名前を変更するには、ソリューション エクスプローラーで名前を右クリックして、[名前の変更] を選択し、名前を選んで F2 キーを押します。または、名前を選んでもう一度選択して入力します。
ファイル内の
Class1
へのすべての参照の名前を変更するかどうかを確認するメッセージが表示される場合があります。 後の手順でコードを置き換えるので、どのように答えてもかまいません。ここで、新しいクラス ライブラリによって公開される API を最初のプロジェクトで使用できるように、プロジェクト参照を追加します。 [Calculator] プロジェクト内の [依存関係] ノードを右クリックし、 [プロジェクト参照の追加] を選択します。
[参照マネージャー] ダイアログ ボックスが表示されます。 このダイアログ ボックスで、他のプロジェクトへの参照、自分のプロジェクトに必要なアセンブリと COM DLL を追加できます。
[参照マネージャー] ダイアログ ボックスで、 [CalculatorLibrary] プロジェクトのチェックボックスをオンにしてから、 [OK] を選択します。
プロジェクト参照は、ソリューション エクスプローラーで [プロジェクト] ノードの下に表示されます。
Program.cs で、
Calculator
クラスとそのすべてのコードを選択し、CTRL+X キーを押してそれを切り取ります。 その後、CalculatorLibrary.cs で、CalculatorLibrary
名前空間にコードを貼り付けます。また、Calculator クラスの前に
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; } } }
Program.cs にも参照がありますが、
Calculator.DoOperation
呼び出しが解決されないというエラーが表示されます。 このエラーは、CalculatorLibrary
が別の名前空間にあるためです。 完全修飾参照の場合は、CalculatorLibrary
名前空間をCalculator.DoOperation
呼び出しに追加できます。result = CalculatorLibrary.Calculator.DoOperation(cleanNum1, cleanNum2, op);
using
ディレクティブをファイルの先頭に追加してみることもできます。using CalculatorLibrary;
using
ディレクティブを追加すると、呼び出しサイトからCalculatorLibrary
名前空間を削除できるようになるはずですが、今度はあいまいさが生じます。Calculator
はCalculatorLibrary
内のクラスですか、それともCalculator
は名前空間ですか?あいまいさを解決するには、Program.cs の名前空間の名前を
Calculator
からCalculatorProgram
に変更します。namespace CalculatorProgram
.NET ライブラリを参照する: ログに書き込む
.NET Trace クラスを使用して、すべての操作のログを追加し、それをテキスト ファイルに書き込むことができます。 Trace
クラスは、基本的な印刷デバッグ手法にも役立ちます。 Trace
クラスは System.Diagnostics
にあり、StreamWriter
のような System.IO
クラスが使用されます。
まず、CalculatorLibrary.cs の先頭に
using
ディレクティブを追加します。using System.IO; using System.Diagnostics;
このように
Trace
クラスを使用する場合は、ファイルストリームに関連付けられているクラスの参照を保持する必要があります。 そうする必要があるのは、電卓がオブジェクトとしてより適切に動作するからです。そのため、CalculatorLibrary.cs のCalculator
クラスの先頭にコンストラクターを追加します。また、
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) {
各計算にログ出力を追加します。 これで、
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; }
Program.cs に戻ると、赤い波状の下線で静的呼び出しにフラグが付けられています。 エラーを修正するには、
while (!endApp)
ループの直前に次のコード行を追加して、calculator
変数を作成します。Calculator calculator = new Calculator();
また、
DoOperation
呼び出しサイトを変更し、小文字のcalculator
という名前のオブジェクトを参照します。 これで、コードは静的メソッドの呼び出しではなく、メンバー呼び出しになります。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 はこのコードのようになるはずです。
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;
}
}
}
.NET Trace クラスを使用して、すべての操作のログを追加し、それをテキスト ファイルに書き込むことができます。 Trace
クラスは、基本的な印刷デバッグ手法にも役立ちます。 Trace
クラスは System.Diagnostics
にあり、StreamWriter
のような System.IO
クラスが使用されます。
まず、CalculatorLibrary.cs の先頭に
using
ディレクティブを追加します。using System.Diagnostics;
このように
Trace
クラスを使用する場合は、ファイルストリームに関連付けられているクラスの参照を保持する必要があります。 そうする必要があるのは、電卓がオブジェクトとしてより適切に動作するからです。そのため、CalculatorLibrary.cs のCalculator
クラスの先頭にコンストラクターを追加します。また、
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) {
各計算にログ出力を追加します。 これで、
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; }
Program.cs に戻ると、赤い波状の下線で静的呼び出しにフラグが付けられています。 エラーを修正するには、
while (!endApp)
ループの直前に次のコード行を追加して、calculator
変数を作成します。Calculator calculator = new Calculator();
また、
DoOperation
呼び出しサイトを変更し、小文字のcalculator
という名前のオブジェクトを参照します。 これで、コードは静的メソッドの呼び出しではなく、メンバー呼び出しになります。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 はこのコードのようになるはずです。
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 で操作を出力するために、Newtonsoft.Json NuGet パッケージを参照できます。 NuGet パッケージは、.NET クラス ライブラリの主な配布方法です。
ソリューション エクスプローラーで、 [CalculatorLibrary] プロジェクトの [依存関係] ノードを右クリックし、 [NuGet パッケージの管理] を選択します。
NuGet パッケージ マネージャーが開きます。
Newtonsoft.Json パッケージを検索して選択し、 [インストール] を選びます。
Visual Studio により、パッケージがダウンロードされ、プロジェクトに追加されます。 ソリューション エクスプローラーの [参照] ノードに、新しいエントリが表示されます。
変更を受け入れるかどうかを確認するメッセージが表示されたら、[OK] を選択します。
Visual Studio により、パッケージがダウンロードされ、プロジェクトに追加されます。 ソリューション エクスプローラーの [パッケージ] ノードに、新しいエントリが表示されます。
CalculatorLibrary.cs の先頭に
Newtonsoft.Json
用のusing
ディレクティブを追加します。using Newtonsoft.Json;
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(); }
DoOperation
メソッドを変更して、JSONwriter
コードを追加します。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 構文を完了するためのメソッドを追加します。
public void Finish() { writer.WriteEndArray(); writer.WriteEndObject(); writer.Close(); }
Program.cs の末尾にある
return;
の前に、Finish
への呼び出しを追加します。// 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 で、次のコード行の左側にある gutter をクリックします。 また、行をクリックして F9 キーを押すか、行を右クリックして [ブレークポイント]>[ブレークポイントの挿入] の順に選択することもできます。
result = calculator.DoOperation(cleanNum1, cleanNum2, op);
表示される赤い点はブレークポイントを示しています。 ブレークポイントを使用すると、アプリを一時停止して、コードを調べることができます。 コードの任意の実行可能な行に、ブレークポイントを設定できます。
アプリケーションをビルドし、実行します。 計算の以下の値を入力します。
- 最初の数値には、「8」と入力します。
- 2 番目の数値には、「0」と入力します。
- 演算子については、どうしましょう。 「d」と入力します。
アプリは、ブレークポイントを作成した場所で一時停止します。これは、左側の黄色いポインターと、強調表示されたコードによって示されます。 強調表示されたコードはまだ実行されていません。
これで、アプリは一時停止しているので、アプリケーションの状態を調べることができます。
デバッグ: 変数を表示する
強調表示されたコードで、
cleanNum1
やop
などの変数をポイントします。 これらの変数の現在の値 (それぞれ8
とd
) が、データヒントに表示されます。デバッグ時には、変数に予期した値が保持されているかどうかを確認することが、問題の修正に不可欠なことがよくあります。
下のペインで、 [ローカル] ウィンドウを確認します。 閉じている場合は、 [デバッグ]>[ウィンドウ]>[ローカル] の順に選択して開きます。
[ローカル] ウィンドウには、現在スコープ内にある各変数およびその値と型が表示されます。
[自動変数] ウィンドウを見てください。
[自動変数] ウィンドウは [ローカル] ウィンドウと似ていますが、アプリが一時停止している現在のコード行の直前と直後の変数が表示されます。
Note
[自動変数] ウィンドウが表示されない場合は、[デバッグ]>[ウィンドウ]>[自動変数] を選んで開きます。
次に、デバッガーでコードを一度に 1 ステートメントだけ実行します。これは "ステップ実行" と呼ばれます。
デバッグ: コードをステップ実行する
F11 キーを押すか、 [デバッグ]>[ステップ イン] の順に選択します。
[ステップ イン] コマンドを使用すると、アプリの現在のステートメントが実行された後、次の実行可能なステートメント (通常は次のコード行) に進みます。 左側の黄色いポインターは、常に現在のステートメントを示しています。
Calculator
クラスのDoOperation
メソッドにステップ インしました。階層的なプログラム フローを確認するには、 [呼び出し履歴] ウィンドウを見ます。 閉じている場合は、 [デバッグ]>[ウィンドウ]>[呼び出し履歴] の順に選択して開きます。
このビューには、黄色のポインターで示される現在の
Calculator.DoOperation
メソッドが表示されます。 2 行目には、Program.cs のMain
メソッドから、メソッドを呼び出した関数が示されています。[呼び出し履歴] ウィンドウには、メソッドおよび関数が呼び出されている順番が表示されます。 また、このウィンドウでは、ショートカット メニューから、 [ソース コードへ移動] など、デバッガーの多くの機能にアクセスできます。
アプリが
switch
ステートメントで一時停止するまで数回、F10 キーを押すか、 [デバッグ]>[ステップ オーバー] の順に選択します。switch (op) {
[ステップ オーバー] コマンドは [ステップ イン] コマンドに似ていますが、現在のステートメントで関数を呼び出す場合、その関数のコードはデバッガーによって実行され、関数から戻るまで実行が中断されない点が異なります。 特定の関数に関心がない場合は、ステップ インよりもステップ オーバーの方が高速です。
もう一度 F10 キーを押して、アプリが次のコード行で一時停止するようにします。
if (num2 != 0) {
このコードにおいては、0 による除算のケースが調べられてます。 アプリを続行すると、一般的例外 (エラー) がスローされますが、実際に返される値をコンソールで表示するなど、他の何らかの操作を試すことができます。 1 つのオプションとして、デバッガーの ''エディット コンティニュ'' と呼ばれる機能を使用して、コードを変更してからデバッグを続けることができます。 しかし、実行フローを一時的に変更するための別の方法があります。
デバッグ: 一時的な変更をテストする
現在は
if (num2 != 0)
ステートメントで一時停止している黄色のポインターを選択し、それを次のステートメントにドラッグします。result = num1 / num2;
ここにポインターをドラッグすると、アプリでは
if
ステートメントを完全にスキップするため、ゼロで除算した場合に何が起きるかを確認できます。F10 キーを押してコード行を実行します。
result
変数をポイントすると、Infinity の値が表示されます。 C# では、Infinity は 0 で除算した場合の結果です。F5 キーを押すか、 [デバッグ]>[デバッグの続行] の順に選択します。
算術演算の結果として、無限大記号がコンソールに表示されます。
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;
}
}
}
次の手順
これでこのチュートリアルは完了です。 さらに学習する場合は、引き続き以下のコンテンツをご覧ください。