IRONSOFTWAREHOME

C#中的異常處理

C# Exception Handling

Tim Corey

59m 46s

例外處理是穩健應用程式開發的重要方面。 Tim Corey的影片"處理C#中的例外 - 何時捕捉,何處捕捉,及如何捕捉",詳細解釋了什麼是例外,如何處理,以及何處處理。

本文旨在使用Tim Corey的影片來解釋C#中的例外處理。 這是一個強大的功能,允許開發者管理程式執行中出現的錯誤和異常狀況。 透過使用try, catch, 和 finally區塊,C#提供了一種結構化的方式來處理執行期間錯誤、記錄例外,並維持程式流程。

簡介

Tim一開始解釋許多開發者對例外和其處理有不正確的觀點。 他強調了解例外是什麼及正確處理它們的何處與如何的重要性,以便建立更穩健的應用程式。

建立示範控制台應用程式

Tim在Visual Studio 2017中建立了一個控制台應用程式以演範例外處理。 他推薦使用控制台應用程式來測試新主題,因為它們設置簡單且易於使用。

using System;

namespace ExceptionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Placeholder for input and output operations
            Console.ReadLine();
        }
    }
}

建立類庫

Tim將一個類庫新增到解決方案中,以模擬現實世界中不同方法彼此調用的場景。

他刪除了預設類,並建立了一個新的類名為DemoCode。

public class DemoCode
{
    // Method to retrieve a number based on the provided position
    public int GetNumber(int position)
    {
        int[] numbers = { 1, 4, 7, 2 };
        return numbers[position];
    }

    // Intermediate method calls GetNumber
    public int ParentMethod(int position)
    {
        return GetNumber(position);
    }

    // Top-level method calls ParentMethod
    public int GrandparentMethod(int position)
    {
        return ParentMethod(position);
    }
}

DemoCode類包含一些彼此調用的方法,最終根據給定位置從陣列中檢索一個數字。

模擬例外

Tim解釋該應用程式旨在展示失敗而非成功。 他通過向GrandparentMethod傳遞一個無效位置引入了一個越界例外。

DemoCode demo = new DemoCode();
int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
Console.WriteLine($"The value at the given position is {result}");

使用無效位置運行上述程式碼會導致IndexOutOfRangeException。 Tim展示了如何使用Visual Studio除錯器突出顯示問題並提供有關例外的詳細資訊。

如何不使用try-catch

Tim解釋開發者在首次學習try-catch區塊時常犯的一個錯誤。 他們通常將整個可能發生例外的程式碼段包裹起來,這可能導致不當處理。

try
{
    int output = 0;
    output = numbers[position];
    return output;
}
catch (Exception ex)
{
    // Avoid returning default values that can mask the problem
    return 0;
}

Tim指出,這種方法有問題,因為它隱藏了例外,並在錯誤假設下繼續執行。 例如,返回0作為預設值可能不合適,並可能導致進一步問題。

正確的例外處理

Tim強調例外提供了有關應用程式中意外狀態的關鍵資訊。 如果應用程式在這種狀態下繼續而沒有適當處理,可能會導致進一步錯誤和資料損壞。

而不是吞下例外,必須妥善處理它們。 這是一個更好的方法:

try
{
    return numbers[position];
}
catch (Exception ex)
{
    // Log the exception or handle it appropriately
    Console.WriteLine(ex.Message);
    throw; // Re-throw the exception to be handled by a higher-level handler
}

通過重新拋出例外,您確保該問題得以傳播,並在必要時可以在更高的層次進行處理。

提供有用的資訊給使用者

Tim解釋說,雖然某些例外可以優雅地處理而不崩潰應用程式,但提供有用的反饋給使用者是很重要的。 例如,顯示一個訊息框或通知,提供重試操作的選項。

更有用的資訊:StackTrace

Tim演示了如何使用例外物件的StackTrace屬性來獲取例外發生的詳細資訊。 這包括類別、方法和行號,這對於除錯是無價的。

try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
    throw;
}

StackTrace屬性提供了一個完整的呼叫堆疊追蹤,幫助開發者精確定位問題的具體位置。

正確放置try-catch

Tim解釋說,正確處理例外不僅僅是捕捉它們,還要知道放置try-catch區塊的位置。 關鍵是將try-catch區塊置於您有足夠上下文可以適當處理例外的層次。

例外放置的不良示範

將一個try-catch區塊放置於呼叫堆疊的深處通常無法有效處理例外,因為您缺乏高層操作的上下文。

// Deep level exception handling (not ideal)
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw;
}

例外放置的良好示範

將try-catch區塊放置於頂層,例如在使用者介面或應用程式的入口點,可以讓您在完整的操作上下文中處理例外。

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

這樣,您可以向使用者提供更有資訊量的訊息,並決定應用程式是否能夠繼續運行或應該被終止。

堆疊追蹤資訊

Tim強調堆疊追蹤資訊在診斷例外中的重要性。 堆疊追蹤提供了詳細的呼叫歷史,展範例外發生的位置和導致它的方式鏈。

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

這些輸出給出了例外的確切位置和通過程式碼的路徑,使得除錯和修復問題更加容易。

處理邏輯示範

Tim展示了如何在適當的層次處理邏輯。 例如,如果一個方法負責打開和關閉資料庫連接,它應該處理例外以確保資源得到了妥善管理。

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        Console.WriteLine("Close database connection");
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Ensure the exception is propagated
    }
}

在此範例中,如果發生例外,(那麼)資料庫連接未能正確關閉,可能導致潛在的資源洩漏。 透過新增一個try-catch區塊,您可以確保即使發生例外的情況下,連接也會關閉。

使用finally區塊

Tim介紹了finally區塊,其確保了無論例外發生與否,某些程式碼會運行。 這對於資源清理特別有用,例如關閉資料庫連接。

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Re-throw the exception to ensure it's handled by a higher-level handler
    }
    finally
    {
        Console.WriteLine("Close database connection");
    }
}

finally區塊在try和catch區塊後運行,確保即使發生例外,也會關閉連接。

throw語句

Tim解釋了重新拋出例外的重要性,以便將它們傳達到呼叫堆疊上層。 這允許高階處理器能夠適當處理例外。

catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw; // Re-throws the exception to be handled by the calling method
}

以throw;重新拋出例外 確保完整的堆疊追蹤得到保留,為除錯提供寶貴的上下文。

正確提升例外

Tim演示了例外如何在呼叫堆疊中逐層冒泡。 每個方法檢查try-catch區塊,然後要麼處理例外,要麼將它們傳遞給調用者。

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

在此範例中,GrandparentMethod捕捉例外,記錄它並重新拋出。 然後控制台應用程式中的頂層try-catch區塊處理例外並顯示錯誤訊息和堆疊追蹤。

例外處理的常見錯誤

Tim指出開發者在處理例外時常犯的幾個錯誤:

  1. 使用 throw ex;:

    • 重寫堆疊追蹤並丟失寶貴上下文。

    • 範例:

    catch (Exception ex)
    {
        // Incorrect
        throw ex; // Rewrites stack trace
    }
    C#
  2. 拋出新的例外

    • 建立帶有自訂訊息的新例外,但丟失原始堆疊追蹤。

    • 範例:

    catch (Exception ex)
    {
        // Incorrect
        throw new Exception("I blew up");
    }
    C#

建立不丟失原始堆疊追蹤的新例外

Tim解釋了如何在保留原始堆疊追蹤的情況下建立新的例外。 當您想提供更具意義的錯誤訊息或不同的例外型別時,這可能很有用,同時仍然保留原始錯誤的上下文。

catch (Exception ex)
{
    throw new ArgumentException("You passed in bad data", ex);
}

透過將原始例外(ex)作為內層例外傳遞,您保留原始堆疊追蹤,這點對於除錯至關重要。

保留堆疊追蹤資訊

Tim演示了在建立新例外時,如何存取原始例外的訊息和堆疊追蹤。

catch (Exception ex)
{
    Console.WriteLine("You passed in bad data");
    Console.WriteLine(ex.StackTrace);
    throw new ArgumentException("You passed in bad data", ex);
}

這確保在堆疊上層拋出的例外包含新的訊息和原始例外細節。

迴圈處理內部例外

Tim提供了一種方法來迴圈處理所有內部例外,以提取它們的訊息和堆疊追蹤。

catch (Exception ex)
{
    Exception inner = ex;
    while (inner != null)
    {
        Console.WriteLine(inner.StackTrace);
        inner = inner.InnerException;
    }
    throw;
}

該迴圈遍歷每個內部例外,列印其堆疊追蹤,確保所有層次的例外都被考慮到。

區分不同例外的處理

Tim討論了如何使用多個catch區塊來處理不同型別的例外。 這允許根據例外型別進行特定處理。

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

在此範例中,ArgumentException透過列印自定訊息進行具體處理,而所有其他例外則回退到一般處理器,列印例外訊息和堆疊追蹤。

多個catch區塊的順序重要性

Tim強調多個catch區塊順序的重要性。 應該首先捕捉最具體的例外,然後是更一般的例外。

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

如果一個更一般的catch區塊出現在具體之前,將會捕捉所有例外,導致具體的catch區塊永遠無法到達,進而導致編譯錯誤。

結論

Tim Corey的高級影片指南介紹了C#中例外處理的重要技術,例如建立新例外、保留堆疊追蹤、及有效使用多個catch區塊。 遵循他的最佳實踐,開發者可以建立穩健的應用程式,優雅地處理例外並提供具有價值的除錯資訊。

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即免費取得 30 天試用金鑰

Your trial license will be sent to your email address

無任何限制。100% 解鎖。無需信用卡。

OR
bullet_checked無需信用卡或建立帳號無任何限制。100% 解鎖。無需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
獲取您的無義務諮詢
填寫以下表格或發送電子郵件至sales@ironsoftware.com
您的詳細資訊將始終保密。
被全球數百萬工程師信任
Iron Software的客戶標誌
立即獲取您的30天試用金鑰
無需信用卡或帳戶建立