演習 - C# コンソール アプリケーションで例外をスローしてキャッチする

完了

この演習では、トップレベルのステートメントで try コード ブロックと catch 句を作成し、MakeChange メソッドで例外を作成してスローし、例外オブジェクトを使用して catch コード ブロックを完了します。 この演習では、次のタスクを実行します。

  1. トップレベルのステートメントの更新す: トップレベルのステートメントに try-catch パターンを実装します。 try コード ブロックには、MakeChange への呼び出しが含まれます。
  2. MakeChange メソッドの更新: "Insufficient till" および "Underpayment" の問題に対する例外を作成してスローします。
  3. スローされた例外のプロパティを使用するように catch コード ブロックを更新します。
  4. 検証テスト: この演習で開発したコードの検証テストを行います。

トップレベルのステートメントに try-catch パターンを追加する

このタスクでは、MakeChange メソッドの呼び出しを try ステートメント内に囲み、対応する catch 句を作成します。

  1. Visual Studio Code エディターで Program.cs ファイルが開かれていることを確認します。

  2. 次のコード行を見つけます。

    // MakeChange manages the transaction and updates the till 
    string transactionMessage = MakeChange(itemCost, cashTill, paymentTwenties, paymentTens, paymentFives, paymentOnes);
    
    // Backup Calculation - each transaction adds current "itemCost" to the till
    if (transactionMessage == "transaction succeeded")
    {
        Console.WriteLine($"Transaction successfully completed.");
        registerCheckTillTotal += itemCost;
    }
    else
    {
        Console.WriteLine($"Transaction unsuccessful: {transactionMessage}");
    }
    
  3. このコードの目的について考えてみましょう。

    MakeChange は、文字列値を返すことに注意してください。 戻り値は、transactionMessage という名前の変数に割り当てられます。 transactionMessage が "transaction succeeded" と等しい場合、購入された商品の代金が registerCheckTillTotal に追加されます。 変数 registerCheckTillTotal は、MakeChange メソッドによって計算されたレジ残高を確認するために使用されます。

  4. MakeChange メソッドの呼び出しを try ステートメント コード ブロックで囲むには、コードを次のように更新します。

    try
    {
        // MakeChange manages the transaction and updates the till 
        string transactionMessage = MakeChange(itemCost, cashTill, paymentTwenties, paymentTens, paymentFives, paymentOnes);
    }
    
  5. catch ステートメント コード ブロックの後に次の try 句を追加します。

    catch
    {
    }
    

    例外を作成してスローしたら、catch 句の開発は完了です。

MakeChange メソッドで例外を作成してスローする

このタスクでは、MakeChange を更新して、トランザクションを完了できない場合にカスタム例外を作成してスローします。

MakeChange メソッドには、例外を発生させる必要がある次の 2 つの問題が含まれています。

  • Underpayment 問題: この問題は、顧客が商品代金よりも少ない金額を支払った場合に発生します。 顧客が十分な金額を支払わなかった場合、MakeChange では例外をスローする必要があります。

  • Insufficient till 問題: この問題は、おつりに必要な紙幣がレジにない場合に発生します。 レジにおつりがない場合、MakeChange では例外をスローする必要があります。

  1. 下にスクロールして MakeChange メソッドに移動します。

  2. 次のコード行を見つけます。

    if (changeNeeded < 0)
        transactionMessage = "Not enough money provided.";
    
  3. このコードで対処しようとしている問題について考えてみましょう。

    changeNeeded が 0 未満の場合、顧客は、購入しようとしている商品の購入価格に充当する十分な金額を支払っていません。 購入価格と顧客が支払った金額は、MakeChange メソッドのパラメーターです。 顧客が十分な金額を支払わない場合、このメソッドはトランザクションを完了できません。 つまり、操作は失敗します。

    これらの条件に一致すると思われる例外の種類が 2 つあります。

    • InvalidOperationException: InvalidOperationException 例外は、メソッドの動作条件が特定のメソッド呼び出しの正常な完了をサポートしていない場合にのみスローする必要があります。 この場合、このメソッドに提供されたパラメーターによって動作条件が確立されます。
    • ArgumentOutOfRangeException - ArgumentOutOfRangeException 例外は、引数の値が、呼び出されたメソッドによって定義された値の許容範囲外にある場合にのみスローする必要があります。 この場合、支払われた金額は商品代金よりも多くなければなりません。

    どちらの種類の例外も機能する可能性はありますが、このアプリケーションのコンテキストでは、わずかですが、InvalidOperationException の方がより適しています。

  4. コードを次のように更新します。

    if (changeNeeded < 0)
        throw new InvalidOperationException("InvalidOperationException: Not enough money provided to complete the transaction.");
    
  5. 下にスクロールして、次のコード行を見つけます。

    if (changeNeeded > 0)
        transactionMessage = "Can't make change. Do you have anything smaller?";
    
  6. このコードで対処しようとしている問題について考えてみましょう。

    おつりを準備する changeNeeded ループの後で while が 0 より大きい場合、レジには、おつりに使用できる紙幣が不足しています。 おつりに必要な紙幣がレジにない場合、メソッドはトランザクションを完了できません。 つまり、操作は失敗します。

    例外を作成するには、InvalidOperationException 例外を使用する必要があります。

  7. コードを次のように更新します。

    if (changeNeeded > 0)
        throw new InvalidOperationException("InvalidOperationException: The till is unable to make the correct change.");
    

catch コード ブロックを完了する

このタスクでは、catch 句を更新して、特定の例外の種類をキャッチします。

  1. MakeChange メソッドより上にスクロールし、次のコードを見つけます。

    catch
    {
    }    
    
  2. MakeChange メソッドでスローされた例外の種類をキャッチするには、コードを次のように更新します。

    catch (InvalidOperationException e)
    {
        Console.WriteLine($"Could not complete transaction: {e.Message}");
    }    
    

    InvalidOperationException でスローされた MakeChange 例外オブジェクトはキャッチされますが、他の例外の種類はキャッチされません。 他の例外の種類を処理する準備ができていないため、それらがコール スタックの下位でキャッチされるようにすることが重要です。 MakeChange 内に他の例外の種類が想定されることに気付いた場合は、さらに catch 句を追加できます。

  3. [ファイル] メニューを使用して更新を保存します。

MakeChange メソッドを "string" から "void" に変換し、例外プロパティにアクセスする

このタスクでは、MakeChangevoid 型になるように更新し、例外プロパティを使用して問題の詳細をユーザーに伝えます。

  1. MakeChange メソッドの一番上までスクロールします。

  2. MakeChange メソッドを string 型から void 型に変換するには、コードを次のように更新します。

    static void MakeChange(int cost, int[] cashTill, int twenties, int tens = 0, int fives = 0, int ones = 0)
    
  3. 次の変数宣言を削除します。

    string transactionMessage = "";
    
  4. MakeChange メソッドの一番下までスクロールします。

  5. 次のコードを削除します。

    if (transactionMessage == "")
        transactionMessage = "transaction succeeded";
    
    return transactionMessage;
    
  6. トップレベルのステートメントまで上にスクロールし、try コード ブロックを見つけます。

  7. try コード ブロックを次のように更新します。

    try
    {
        // MakeChange manages the transaction and updates the till 
        MakeChange(itemCost, cashTill, paymentTwenties, paymentTens, paymentFives, paymentOnes);
    
        Console.WriteLine($"Transaction successfully completed.");
        registerCheckTillTotal += itemCost;
    }
    
  8. 次のコード行を見つけて削除します。

    // Backup Calculation - each transaction adds current "itemCost" to the till
    if (transactionMessage == "transaction succeeded")
    {
        Console.WriteLine($"Transaction successfully completed.");
        registerCheckTillTotal += itemCost;
    }
    else
    {
        Console.WriteLine($"Transaction unsuccessful: {transactionMessage}");
    }
    
    

    try および catch コード ブロックは、トランザクションの "成功" および "失敗" のメッセージをユーザーに伝えるようになりました。 問題については、例外の Message プロパティで説明されるため、1 つの Console.WriteLine() ステートメントだけで両方の問題に対処します。 これらの更新後、コードは読みやすくなり、保守も容易になります。

  9. [ファイル] メニューを使用して更新を保存します。

作業を確認

このタスクでは、アプリケーションを実行し、更新されたコードが意図したとおりに動作することを確認します。

  1. 上にスクロールして、トップレベルのステートメントで while ループを見つけます。

    このループは、トランザクションを反復するために使用されます。

  2. while ループの開始行の数行前にある次のコードを見つけます。

    int transactions = 10;
    
    
  3. トランザクションの回数を次のように 40 に更新します。

    int transactions = 40;
    
    
  4. while ループ内で次のコード行を見つけます。

    int itemCost = valueGenerator.Next(2, 20);
    
    
  5. itemCost 乱数ジェネレーターを次のように更新します。

    int itemCost = valueGenerator.Next(2, 50);
    
    

    顧客が購入する商品には、この価格範囲の方がより適しています。

  6. [ファイル] メニューを使用して更新を保存します。

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

  8. [ターミナル] パネルで出力を確認します。

  9. 次の 2 つの例外の種類に関連付けられているメッセージが表示されていることを確認します。

    トランザクション レポートには、次の "Could not complete transaction" というメッセージが含まれているはずです。

    Customer is making a $42 purchase
             Using 2 twenty dollar bills
             Using 0 ten dollar bills
             Using 0 five dollar bills
             Using 0 one dollar bills
    Could not complete transaction: InvalidOperationException: Not enough money provided to complete the transaction.
    
    Customer is making a $23 purchase
             Using 2 twenty dollar bills
             Using 0 ten dollar bills
             Using 0 five dollar bills
             Using 1 one dollar bills
    Cashier prepares the following change:
             A five
             A five
             A one
             A one
    Could not complete transaction: InvalidOperationException: The till is unable to make change for the cash provided.
    
    

これで、コード ロジックの問題を修正するためにキャッシュ レジスタ アプリケーションをデバッグし、適切な例外処理手法を使用するようにアプリケーションを更新しました。

報告された出力は、レジの資金のバランスが取れなくなったことを示しています。 コードには追加のロジック バグがあります。 Visual Studio Code でのデバッグのスキルを実際に試したい場合は、チャレンジ プロジェクト モジュールを利用できます。