演習 - 例外を生成して投げる
開発者は、しばしばメソッド内で例外を作成してスローし、それを呼び出しスタックの後の適切な位置でキャッチして処理する必要があります。 例外処理は、アプリケーションの安定性を確保するのに役立ちます。
この演習では、呼び出されたメソッド内の潜在的なエラー状態を含むサンプル アプリケーションから始めます。 更新されたメソッドは、問題を検出したときに例外を throw します。 例外は、メソッドを呼び出すコードの catch ブロックで処理されます。 その結果、より優れたユーザー エクスペリエンスを提供するアプリケーションが得られます。
新しいコード プロジェクトを作成する
最初の手順では、このモジュールで使用できるコード プロジェクトを作成します。
Visual Studio Code の新しいインスタンスを開きます。
[ファイル] メニューの [フォルダーを開く] を選択します。
[ フォルダーを開く] ダイアログで、Windows デスクトップ フォルダーに移動します。
[ フォルダーを開く ] ダイアログで、[ 新しいフォルダー] を選択します。
新しいフォルダーに ThrowExceptions101 という名前を付け、[フォルダーの選択] を 選択します。
[ターミナル] メニューで、[新しいターミナル] を選択します。
.NET CLI コマンドを使用して、新しいコンソール アプリを作成します。
TERMINAL パネルのコマンド プロンプトで、次のコマンドを入力します。
dotnet new console[ターミナル] パネルを閉じます。
サンプル アプリケーションを確認する
サンプル アプリケーションを読み込んで確認するには、次の手順に従います。
Program.cs ファイルを開きます。
[表示] メニューで [コマンド パレット] を選択してください。
コマンド プロンプトで「.net: g」と入力し、[.NET:ビルドとデバッグ用の資産を生成する] を選択してください。
Program.cs ファイルの内容を次のコードに置き換えます。
// Prompt the user for the lower and upper bounds Console.Write("Enter the lower bound: "); int lowerBound = int.Parse(Console.ReadLine()); Console.Write("Enter the upper bound: "); int upperBound = int.Parse(Console.ReadLine()); decimal averageValue = 0; // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the value returned by AverageOfEvenNumbers in the console Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); // Wait for user input Console.ReadLine(); static decimal AverageOfEvenNumbers(int lowerBound, int upperBound) { int sum = 0; int count = 0; decimal average = 0; for (int i = lowerBound; i <= upperBound; i++) { if (i % 2 == 0) { sum += i; count++; } } average = (decimal)sum / count; return average; }少し時間をかけてコードを確認してください。
アプリケーションが次のタスクを実行していることに注意してください。
最上位レベルのステートメントでは、
Console.ReadLine()ステートメントを使用して、lowerBoundとupperBoundの値を取得します。最上位レベルのステートメントは、
lowerBoundメソッドを呼び出すときにupperBoundとAverageOfEvenNumbersを引数として渡します。AverageOfEvenNumbersメソッドは次のタスクを実行します:- 計算で使用されるローカル変数を宣言します。
forループを使用して、lowerBoundとupperBoundの間の偶数を合計します。 合計はsumに格納されます。- 合計に含まれる数値の数をカウントします。 カウントは
countに格納されます。 - 合計された数値の平均を、
averageという名前の変数に格納します。averageの値が返されます。
最上位レベルのステートメントは、
AverageOfEvenNumbersによって返された値をコンソールに出力し、実行を一時停止します。
デバッグ環境を構成する
サンプル アプリケーションは、コンソールからユーザー入力を読み取ります。 [デバッグ コンソール] パネルでは、コンソールからの入力の読み取りはサポートされていません。 デバッガーでこのアプリケーションを実行するには、launch.json ファイルを更新する必要があります。
エクスプローラー ビューを使用して、launch.json ファイルを開きます。
launch.json ファイルで、
console属性を次のように更新します。// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console":"integratedTerminal",console属性の既定値はinternalConsoleであり、[デバッグ コンソール] パネルに合わせて配置されます。 残念ながら、DEBUG コンソール パネルはコンソール入力をサポートしていません。integratedTerminal設定では、コンソールの入力と出力をサポートする [ターミナル] パネルに合わせて配置されます。launch.json ファイルに変更を保存し、ファイルを閉じます。
Visual Studio Code で [実行] メニューの [デバッグの開始] を選択します。
ターミナル パネルに切り替えます。
"下限" プロンプトで、「3」と入力します。
"上限" プロンプトで、「11」と入力します。
アプリケーションに次のメッセージが表示され、一時停止していることに注意してください。
The average of even numbers between 3 and 11 is 7.アプリケーションを終了するには、Enter キーを押します。
AverageOfEvenNumbers メソッドで例外をスローする
AverageOfEvenNumbers メソッドは、下限より大きい上限を想定しています。 下限が上限以上の場合、 DivideByZero エラーが発生します。
下限が上限以上の場合に例外をスローするには、 AverageOfEvenNumbers メソッドを更新する必要があります。
少し時間を取って、問題に対処する方法を検討してください。
1つのオプションは、
averageコードブロック内でtryの計算をラップし、catch例外が発生したときにDivideByZeroを処理することです。 例外を再スローし、呼び出し元のコードで処理できます。もう 1 つのオプションは、計算を開始する前に入力パラメーターを評価することです。
lowerBoundがupperBound以上の場合は、例外をスローできます。計算を開始する前にパラメーターを評価し、例外をスローすることをお勧めします。
スローする例外の種類を検討します。
この問題に対応する例外の種類は 2 つあります。
ArgumentOutOfRangeException-ArgumentOutOfRangeException例外の種類は、引数の値が、呼び出されたメソッドで定義されている値の許容範囲外にある場合にのみスローする必要があります。AverageOfEvenNumbersはlowerBoundまたはupperBoundの許容範囲を明示的に定義していませんが、lowerBoundの値はupperBoundの許容範囲を意味します。InvalidOperationException:InvalidOperationException例外の種類は、メソッドの動作条件が特定のメソッド呼び出しの正常な完了をサポートしていない場合にのみスローする必要があります。 この場合、動作条件はメソッドの入力パラメーターによって確立されます。
2 つ以上の例外の種類から選択する場合は、問題に合った例外の種類をより厳密に選択します。 この場合、2 つの例外の種類が問題に均等に配置されます。
問題に等しく対応する 2 つ以上の例外の種類がある場合は、最も狭いスコープの例外の種類を選択します。
ArgumentOutOfRangeException例外の種類は、メソッドに渡される引数のスコープです。InvalidOperationException例外の種類のスコープは、メソッドの動作条件で定められています。 この場合、ArgumentOutOfRangeException例外の種類のスコープは、InvalidOperationException例外の種類よりも狭くなります。AverageOfEvenNumbersメソッドからはArgumentOutOfRangeException例外をスローするようにします。AverageOfEvenNumbersメソッドの上部で、上限の問題を検出するには、次のようにコードを更新します。if (lowerBound >= upperBound) { } int sum = 0;ArgumentOutOfRangeException例外を作成してスローするには、ifコード ブロックを次のように更新します。if (lowerBound >= upperBound) { throw new ArgumentOutOfRangeException("upperBound", "ArgumentOutOfRangeException: upper bound must be greater than lower bound."); }このコード行は、例外の原因となる入力パラメーターの名前と指定されたエラー メッセージを使用して、
ArgumentOutOfRangeExceptionクラスの新しいインスタンスを初期化します。
呼び出し元のコードで例外をキャッチする
可能な限り、例外を処理できる呼び出しスタック レベルでキャッチする必要があります。 このサンプル アプリケーションでは、 AverageOfEvenNumbers メソッドのパラメーターを呼び出し元のメソッド (最上位レベルのステートメント) で管理できます。
最上位レベルのステートメントまでスクロールします。
AverageOfEvenNumbersメソッド呼び出しとConsole.WriteLineステートメントをtryコード ブロック内で囲むには、次のようにコードを更新します。try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); }関連付けられている
catch句を作成するには、次のコードを入力します。catch(ArgumentOutOfRangeException ex) { }少し時間を取って、例外を処理する方法を検討してください。
この例外を処理するには、コードで次の操作を行う必要があります。
- ユーザーに問題を説明します。
upperBoundの新しい値を取得します。- 新しい
AverageOfEvenNumbersを使用してupperBoundを呼び出します。 - 指定された新しい
catchがまだupperBound以下の場合は、引き続き例外をlowerBoundします。
例外の
catchを続けるには、ループが必要です。AverageOfEvenNumbersメソッドを少なくとも 1 回呼び出す必要があるため、doループを使用する必要があります。tryループ内でcatchブロックとdoブロックを囲むには、次のようにコードを更新します。do { try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); } catch (ArgumentOutOfRangeException ex) { } }whileループの終了条件を定義するには、do式が必要です。doコード ブロックの内容が定義される前に条件を指定することは困難です。catchコード ブロックを完了すると、必要なwhile式を定義するのに役立ちます。ユーザーに問題を説明し、新しい
upperBoundを取得するには、次のようにcatchコード ブロックを更新します。catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound: "); upperBound = int.Parse(Console.ReadLine()); }更新された
catchコード ブロックでは、問題が説明され、ユーザーは新しい上限を入力する必要があります。 ただし、ユーザーが入力する有効な上限値を持っていない場合はどうでしょうか。 ユーザーが値を入力するのではなく、ループを終了する必要がある場合はどうしますか?新しい上限を入力するのではなく、ループを終了するオプションをユーザーに提供するには、次のように
catchコード ブロックを更新します。catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound (or enter Exit to quit): "); string? userResponse = Console.ReadLine(); if (userResponse.ToLower().Contains("exit")) { } else { upperBound = int.Parse(userResponse); } }更新された
catchコード ブロックには、"exit" パスと "新しい上限" パスの 2 つのパスが含まれています。whileループに必要なdo式を考慮するために、少し時間を取ってください。ユーザーがプロンプトで「終了」と入力した場合、コードはループを終了する必要があります。 ユーザーが新しい上限に入った場合は、ループを続行する必要があります。 ブール値を評価する
while式を使用できます。 例えば次が挙げられます。while (exit == false);提案された
while式は、次の動作を確立します。- ブール
doがexitと等しい限り、falseループは反復処理を続けます。 doループは、ブールexitがtrueと等しいとすぐに反復処理を停止します。
- ブール
exitという名前のブール変数をインスタンス化し、exitを使用してdoループの終了条件を設定するには、次のようにコードを更新します。bool exit = false; do { try { // Calculate the sum of the even numbers between the bounds averageValue = AverageOfEvenNumbers(lowerBound, upperBound); // Display the result to the user Console.WriteLine($"The average of even numbers between {lowerBound} and {upperBound} is {averageValue}."); exit = true; } catch (ArgumentOutOfRangeException ex) { Console.WriteLine("An error has occurred."); Console.WriteLine(ex.Message); Console.WriteLine($"The upper bound must be greater than {lowerBound}"); Console.Write($"Enter a new upper bound (or enter Exit to quit): "); string? userResponse = Console.ReadLine(); if (userResponse.ToLower().Contains("exit")) { exit = true; } else { exit = false; upperBound = int.Parse(userResponse); } } } while (exit == false);更新したコードを保存します。
[実行] メニューで、[デバッグの開始] を選択します。
ターミナル パネルに切り替えます。
"下限" プロンプトで、「3」と入力します。
"上限" プロンプトで、「3」と入力します。
ターミナル パネルに次の出力が表示されます。
Enter the lower bound: 3 Enter the upper bound: 3 An error has occurred. ArgumentOutOfRangeException: upper bound must be greater than lower bound. (Parameter 'upperBound') The upper bound must be greater than 3 Enter a new upper bound (or enter Exit to quit):新しい上限のプロンプトで、「11」と入力します。
ターミナル パネルに次の出力が表示されます。
Enter the lower bound: 3 Enter the upper bound: 3 An error has occurred. ArgumentOutOfRangeException: upper bound must be greater than lower bound. (Parameter 'upperBound') The upper bound must be greater than 3 Enter a new upper bound (or enter Exit to quit): 11 The average of even numbers between 3 and 11 is 7.アプリケーションを終了するには、Enter キーを押します。
おめでとうございます! 例外のスロー、キャッチ、処理に成功しました。
まとめ
このユニットで覚えておく必要があるいくつかの重要な点を次に示します。
- アプリケーションの要件をサポートするようにデバッグ環境が構成されていることを確認します。
- 問題または条件が検出されたときに、メソッド コードから例外をスローするようにします。
- 例外は、解決できる呼び出し履歴のレベルでキャッチする必要があります。