練習:建立和擲回例外狀況

已完成

開發人員通常需要在方法內建立並拋出例外狀況,然後在呼叫堆疊中較深的位置攔截並處理這些例外狀況。 例外狀況處理可協助您確保應用程式的穩定性。

在本練習中,您將從範例應用程式開始,其中包含呼叫方法內的潛在錯誤狀況。 當您的更新方法偵測到問題時,將會 throw 引發例外。 例外狀況將會在呼叫 方法的程式代碼區塊中 catch 處理。 結果是提供更佳用戶體驗的應用程式。

建立新的程式代碼專案

第一個步驟是建立可在本課程模組期間使用的程式代碼專案。

  1. 開啟新的 Visual Studio Code 執行個體。

  2. 在 [檔案] 功能表上,選取 [開啟資料夾]

  3. 在 [ 開啟資料夾] 對話框中,流覽至您的 Windows Desktop 資料夾。

  4. 在 [ 開啟資料夾] 對話框中,選取 [ 新增資料夾]。

  5. 將新資料夾命名為 ThrowExceptions101,然後選取 [ 選取資料夾]。

  6. 在 [終端機] 功能表上,選取 [新增終端機]

    您將使用 .NET CLI 命令來建立新的控制台應用程式。

  7. 在終端機面板命令提示字元中,輸入下列命令:

    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. 使用 EXPLORER 檢視來開啟 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,對齊於偵錯主控台面板。 不幸的是,[偵錯控制台] 面板不支援主控台輸入。 此 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. 請花一分鐘時間來考慮您要如何處理問題。

    其中一個選項是將 average 的計算包裝在 try 程式碼區塊內,並在發生例外狀況時 catchDivideByZero。 您可以重新擲回例外狀況,然後在呼叫的程式碼中加以處理。

    另一個選項是在開始計算之前評估輸入參數。 如果 lowerBound 大於或等於 upperBound,您可以擲回例外狀況。

    在開始計算之前評估參數並擲回例外狀況是較佳的選擇。

  2. 請考慮要擲回的例外狀況類型。

    有兩種與問題相符的例外狀況類型:

    • ArgumentOutOfRangeExceptionArgumentOutOfRangeException- 只有當自變數的值超出叫用方法所定義的允許值範圍之外時,才應該擲回例外狀況類型。 雖然AverageOfEvenNumbers未明確定義lowerBoundupperBound的允許範圍,但lowerBound的值確實暗示了upperBound的允許範圍。
    • InvalidOperationExceptionInvalidOperationException:只有在方法的作業條件不支援特定方法呼叫成功完成時,才應該擲回例外狀況類型。 在此情況下,作業條件是由 方法的輸入參數所建立。

    當您有兩個以上的例外狀況類型可供選擇時,請選取更貼近問題的例外狀況類型。 在此情況下,這兩個例外狀況類型會與問題一致。

    當您有兩個或多個符合問題的例外狀況類型時,請選取範圍最窄的例外狀況類型。 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 方法一次,因此應該使用 do 迴圈。

  5. 若要將 trycatch 區塊包在 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 程式碼區塊包含兩個路徑: 一個「結束」路徑和「新的上限」路徑。

  8. 請花一分鐘來思考 while 迴圈需要的 do 表達式。

    如果使用者在提示字元中輸入 「結束」,程式代碼應該會結束迴圈。 如果使用者輸入新的上限,迴圈應該會繼續。 while一個用於評估布爾值的表達式可以被使用。 例如:

    while (exit == false);
    

    建議的 while 表達式將會建立下列行為:

    • 迴圈do只要布爾值exit等於false就會繼續迭代。
    • 迴圈 do 會在布爾值 exit 等於 true時立即停止反覆執行。
  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。

祝賀! 您已成功擲回、攔截並處理例外狀況。

回顧

以下為本單元須記住的一些重點:

  • 請確定您的偵錯環境已設定為支援您的應用程式需求。
  • 偵測到問題或條件時,方法程式代碼應該擲回例外狀況。
  • 例外狀況應該在可解析的呼叫堆疊層級攔截。