演習 - 例外を生成して投げる

完了

開発者は、しばしばメソッド内で例外を作成してスローし、それを呼び出しスタックの後の適切な位置でキャッチして処理する必要があります。 例外処理は、アプリケーションの安定性を確保するのに役立ちます。

この演習では、呼び出されたメソッド内の潜在的なエラー状態を含むサンプル アプリケーションから始めます。 更新されたメソッドは、問題を検出したときに例外を throw します。 例外は、メソッドを呼び出すコードの catch ブロックで処理されます。 その結果、より優れたユーザー エクスペリエンスを提供するアプリケーションが得られます。

新しいコード プロジェクトを作成する

最初の手順では、このモジュールで使用できるコード プロジェクトを作成します。

  1. Visual Studio Code の新しいインスタンスを開きます。

  2. [ファイル] メニューの [フォルダーを開く] を選択します。

  3. [ フォルダーを開く] ダイアログで、Windows デスクトップ フォルダーに移動します。

  4. [ フォルダーを開く ] ダイアログで、[ 新しいフォルダー] を選択します。

  5. 新しいフォルダーに ThrowExceptions101 という名前を付け、[フォルダーの選択] を 選択します

  6. [ターミナル] メニューで、[新しいターミナル] を選択します。

    .NET CLI コマンドを使用して、新しいコンソール アプリを作成します。

  7. TERMINAL パネルのコマンド プロンプトで、次のコマンドを入力します。

    dotnet new console
    
  8. [ターミナル] パネルを閉じます。

サンプル アプリケーションを確認する

サンプル アプリケーションを読み込んで確認するには、次の手順に従います。

  1. Program.cs ファイルを開きます。

  2. [表示] メニューで [コマンド パレット] を選択してください。

  3. コマンド プロンプトで「.net: g」と入力し、[.NET:ビルドとデバッグ用の資産を生成する] を選択してください。

  4. 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;
    }
    
  5. 少し時間をかけてコードを確認してください。

    アプリケーションが次のタスクを実行していることに注意してください。

    1. 最上位レベルのステートメントでは、 Console.ReadLine() ステートメントを使用して、 lowerBoundupperBoundの値を取得します。

    2. 最上位レベルのステートメントは、lowerBound メソッドを呼び出すときにupperBoundAverageOfEvenNumbersを引数として渡します。

    3. AverageOfEvenNumbers メソッドは次のタスクを実行します:

      1. 計算で使用されるローカル変数を宣言します。
      2. for ループを使用して、lowerBoundupperBoundの間の偶数を合計します。 合計は sumに格納されます。
      3. 合計に含まれる数値の数をカウントします。 カウントは countに格納されます。
      4. 合計された数値の平均を、 averageという名前の変数に格納します。 averageの値が返されます。
    4. 最上位レベルのステートメントは、 AverageOfEvenNumbers によって返された値をコンソールに出力し、実行を一時停止します。

デバッグ環境を構成する

サンプル アプリケーションは、コンソールからユーザー入力を読み取ります。 [デバッグ コンソール] パネルでは、コンソールからの入力の読み取りはサポートされていません。 デバッガーでこのアプリケーションを実行するには、launch.json ファイルを更新する必要があります。

  1. エクスプローラー ビューを使用して、launch.json ファイルを開きます。

  2. launch.json ファイルで、console 属性を次のように更新します。

    // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
    "console":"integratedTerminal",
    

    console 属性の既定値は internalConsole であり、[デバッグ コンソール] パネルに合わせて配置されます。 残念ながら、DEBUG コンソール パネルはコンソール入力をサポートしていません。 integratedTerminal 設定では、コンソールの入力と出力をサポートする [ターミナル] パネルに合わせて配置されます。

  3. launch.json ファイルに変更を保存し、ファイルを閉じます。

  4. Visual Studio Code で [実行] メニューの [デバッグの開始] を選択します。

  5. ターミナル パネルに切り替えます。

  6. "下限" プロンプトで、「3」と入力します。

  7. "上限" プロンプトで、「11」と入力します。

  8. アプリケーションに次のメッセージが表示され、一時停止していることに注意してください。

    The average of even numbers between 3 and 11 is 7.
    
  9. アプリケーションを終了するには、Enter キーを押します。

AverageOfEvenNumbers メソッドで例外をスローする

AverageOfEvenNumbers メソッドは、下限より大きい上限を想定しています。 下限が上限以上の場合、 DivideByZero エラーが発生します。

下限が上限以上の場合に例外をスローするには、 AverageOfEvenNumbers メソッドを更新する必要があります。

  1. 少し時間を取って、問題に対処する方法を検討してください。

    1つのオプションは、averageコードブロック内でtryの計算をラップし、catch例外が発生したときにDivideByZeroを処理することです。 例外を再スローし、呼び出し元のコードで処理できます。

    もう 1 つのオプションは、計算を開始する前に入力パラメーターを評価することです。 lowerBoundupperBound以上の場合は、例外をスローできます。

    計算を開始する前にパラメーターを評価し、例外をスローすることをお勧めします。

  2. スローする例外の種類を検討します。

    この問題に対応する例外の種類は 2 つあります。

    • ArgumentOutOfRangeException - ArgumentOutOfRangeException 例外の種類は、引数の値が、呼び出されたメソッドで定義されている値の許容範囲外にある場合にのみスローする必要があります。 AverageOfEvenNumberslowerBoundまたはupperBoundの許容範囲を明示的に定義していませんが、lowerBoundの値はupperBoundの許容範囲を意味します。
    • InvalidOperationException:InvalidOperationException 例外の種類は、メソッドの動作条件が特定のメソッド呼び出しの正常な完了をサポートしていない場合にのみスローする必要があります。 この場合、動作条件はメソッドの入力パラメーターによって確立されます。

    2 つ以上の例外の種類から選択する場合は、問題に合った例外の種類をより厳密に選択します。 この場合、2 つの例外の種類が問題に均等に配置されます。

    問題に等しく対応する 2 つ以上の例外の種類がある場合は、最も狭いスコープの例外の種類を選択します。 ArgumentOutOfRangeException例外の種類は、メソッドに渡される引数のスコープです。 InvalidOperationException例外の種類のスコープは、メソッドの動作条件で定められています。 この場合、 ArgumentOutOfRangeException 例外の種類のスコープは、 InvalidOperationException 例外の種類よりも狭くなります。

    AverageOfEvenNumbers メソッドからは ArgumentOutOfRangeException 例外をスローするようにします。

  3. AverageOfEvenNumbersメソッドの上部で、上限の問題を検出するには、次のようにコードを更新します。

    if (lowerBound >= upperBound)
    {
    
    }
    
    int sum = 0;    
    
  4. ArgumentOutOfRangeException 例外を作成してスローするには、if コード ブロックを次のように更新します。

    if (lowerBound >= upperBound)
    {
        throw new ArgumentOutOfRangeException("upperBound", "ArgumentOutOfRangeException: upper bound must be greater than lower bound.");
    }
    

    このコード行は、例外の原因となる入力パラメーターの名前と指定されたエラー メッセージを使用して、 ArgumentOutOfRangeException クラスの新しいインスタンスを初期化します。

呼び出し元のコードで例外をキャッチする

可能な限り、例外を処理できる呼び出しスタック レベルでキャッチする必要があります。 このサンプル アプリケーションでは、 AverageOfEvenNumbers メソッドのパラメーターを呼び出し元のメソッド (最上位レベルのステートメント) で管理できます。

  1. 最上位レベルのステートメントまでスクロールします。

  2. 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}.");
    }
    
  3. 関連付けられている catch 句を作成するには、次のコードを入力します。

    catch(ArgumentOutOfRangeException ex)
    {
    
    }
    
  4. 少し時間を取って、例外を処理する方法を検討してください。

    この例外を処理するには、コードで次の操作を行う必要があります。

    • ユーザーに問題を説明します。
    • upperBoundの新しい値を取得します。
    • 新しいAverageOfEvenNumbersを使用してupperBoundを呼び出します。
    • 指定された新しいcatchがまだupperBound以下の場合は、引き続き例外をlowerBoundします。

    例外の catch を続けるには、ループが必要です。 AverageOfEvenNumbers メソッドを少なくとも 1 回呼び出す必要があるため、do ループを使用する必要があります。

  5. 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式を定義するのに役立ちます。

  6. ユーザーに問題を説明し、新しい 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 コード ブロックでは、問題が説明され、ユーザーは新しい上限を入力する必要があります。 ただし、ユーザーが入力する有効な上限値を持っていない場合はどうでしょうか。 ユーザーが値を入力するのではなく、ループを終了する必要がある場合はどうしますか?

  7. 新しい上限を入力するのではなく、ループを終了するオプションをユーザーに提供するには、次のように 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 つのパスが含まれています。

  8. while ループに必要なdo式を考慮するために、少し時間を取ってください。

    ユーザーがプロンプトで「終了」と入力した場合、コードはループを終了する必要があります。 ユーザーが新しい上限に入った場合は、ループを続行する必要があります。 ブール値を評価する while 式を使用できます。 例えば次が挙げられます。

    while (exit == false);
    

    提案された while 式は、次の動作を確立します。

    • ブールdoexitと等しい限り、false ループは反復処理を続けます。
    • do ループは、ブールexittrueと等しいとすぐに反復処理を停止します。
  9. 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);
    
  10. 更新したコードを保存します。

  11. [実行] メニューで、[デバッグの開始] を選択します。

  12. ターミナル パネルに切り替えます。

  13. "下限" プロンプトで、「3」と入力します。

  14. "上限" プロンプトで、「3」と入力します。

  15. ターミナル パネルに次の出力が表示されます。

    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):
    
  16. 新しい上限のプロンプトで、「11」と入力します。

  17. ターミナル パネルに次の出力が表示されます。

    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.
    
  18. アプリケーションを終了するには、Enter キーを押します。

おめでとうございます! 例外のスロー、キャッチ、処理に成功しました。

まとめ

このユニットで覚えておく必要があるいくつかの重要な点を次に示します。

  • アプリケーションの要件をサポートするようにデバッグ環境が構成されていることを確認します。
  • 問題または条件が検出されたときに、メソッド コードから例外をスローするようにします。
  • 例外は、解決できる呼び出し履歴のレベルでキャッチする必要があります。