首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 異常處理完整指南:建立、擲回與捕捉異常的最佳實務

     編輯:關於C語言

前言

在 C# 程式開發中,異常處理是確保應用程式穩健性的關鍵機制。正確地建立、擲回與處理異常,不僅能讓程式在發生錯誤時優雅地恢復,更能提供清晰的診斷資訊,協助開發者快速定位問題。本文將深入探討異常的生命週期、何時該擲回異常、自訂異常類別的設計原則,以及 try/catch/finally 區塊的進階用法。

一、建立與擲回異常

異常代表執行期間發生的錯誤狀況。當方法無法完成其既定功能時,應建立描述錯誤的異常物件,並透過 throw 關鍵字擲回。執行階段會沿著呼叫堆疊尋找相容的異常處理常式。

1. 方法無法完成功能時

最常見的情況是參數驗證失敗。例如傳入 null 參數時,應擲回 ArgumentNullException 或 ArgumentException:

static void CopyObject(SampleClass original)
{
    if (original == null)
    {
        throw new ArgumentException("參數不可為 null", nameof(original));
    }
    // 正常邏輯...
}

2. 物件狀態不允許操作時

當物件處於無法執行特定作業的狀態(如嘗試寫入唯讀檔案),應擲回 InvalidOperationException 或其衍生類別:

class ProgramLog
{
    FileStream logFile = null;

    void WriteLog()
    {
        if (!logFile.CanWrite)
        {
            throw new InvalidOperationException("記錄檔不可為唯讀");
        }
        // 寫入邏輯...
    }
}

3. 參數導致底層異常時

若方法參數觸發了底層異常(如索引越界),應捕捉原始異常並包裝為 ArgumentException,同時透過 InnerException 保留原始錯誤資訊:

static int GetValueFromArray(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException ex)
    {
        throw new ArgumentException("索引超出範圍", nameof(index), ex);
    }
}

異常物件的關鍵屬性

  • StackTrace:自動記錄從 throw 點開始的呼叫堆疊,包含方法名稱、檔案名與行號。
  • Message:說明異常原因的字串,切勿包含敏感資訊。
  • ParamName(ArgumentException 專屬):導致異常的參數名稱,屬性設定子應設為 value。

擲回異常的禁忌

  • 不要用異常控制正常程式流程,僅用於錯誤情況。
  • 不要將異常作為傳回值或參數傳遞。
  • 避免在原始碼中刻意擲回 System.Exception、System.SystemException、NullReferenceException 或 IndexOutOfRangeException。
  • 不要建立僅在偵錯模式擲回的異常,改用 Debug.Assert。

二、定義自訂異常類別

雖然 .NET 提供豐富的內建異常,但特定業務領域常需要自訂異常以精確表達錯誤語意。自訂異常類別應繼承 Exception,並至少實作四個建構函式:

[Serializable]
public class InvalidDepartmentException : Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
    
    // 序列化建構函式(跨應用程式域傳遞時必要)
    protected InvalidDepartmentException(SerializationInfo info, StreamingContext context) 
        : base(info, context) { }
}

僅在新增屬性能協助解決異常時才加入屬性,並覆寫 ToString() 包含額外資訊。

三、異常處理:try/catch/finally

C# 使用 try 區塊包裹可能擲回異常的程式碼,搭配 catch 處理異常,finally 則確保清理程式碼一定執行。

基本語法結構

// 僅 catch
try { /* 可能擲回異常的程式碼 */ }
catch (SpecificException ex) { /* 處理 */ }

// 僅 finally
try { /* 程式碼 */ }
finally { /* 清理資源 */ }

// 完整形式
try { /* 程式碼 */ }
catch (SpecificException ex) { /* 處理 */ }
finally { /* 無論是否發生異常皆執行 */ }

Catch 區塊的最佳實務

  • 僅捕捉知道如何處理的特定異常類型。
  • 避免捕捉基底 System.Exception 且不重新擲回。
  • 多個 catch 由上而下評估,最符合的衍生類別優先,故具體類別應放在前面。

何時應捕捉異常?

  1. 能具體恢復:如捕捉 FileNotFoundException 後提示使用者重新輸入檔名。
  2. 包裝並擲回更具體異常:將底層異常轉換為較高層級的語意異常。
  3. 部分處理後重新擲回:如記錄錯誤日誌後再 throw; 保留原始堆疊。
try
{
    // 存取資源
}
catch (UnauthorizedAccessException e)
{
    LogError(e);  // 記錄日誌
    throw;        // 重新擲回原始異常,保留堆疊追蹤
}

Finally 區塊:資源釋放的保障

finally 區塊會在 try 與 catch 完成後執行,無論是否發生異常。適用於釋放非受控資源(檔案流、資料庫連線、圖形控制代碼等),不必等待記憶體回收。

FileStream file = null;
FileInfo fileInfo = new FileInfo(@"C:\file.txt");

try
{
    file = fileInfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    if (file != null)  // OpenWrite 可能失敗,檢查 null 避免 NullReferenceException
    {
        file.Close();
    }
}

同樣適用於資料庫連線關閉,避免連線池耗盡。

總結

掌握異常的建立、擲回與處理,是撰寫高品質 C# 程式的核心能力。遵循「具體異常、保留堆疊、finally 清理資源」三大原則,能讓您的應用程式在面對錯誤時既穩健又易於維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved