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

C# 異常處理完整指南:try-catch-finally、自訂異常與最佳實踐

     編輯:關於C語言

異常處理核心機制

C# 使用 try、catch、finally 三大關鍵字構建結構化異常處理(SEH):

  • try:包裹可能拋出異常的程式碼區塊
  • catch:捕獲特定類型異常並處理
  • finally:無論是否發生異常都會執行,用於資源清理

基本語法與流程

static double SafeDivision(double x, double y)
{
    if (y == 0)
        throw new DivideByZeroException("除數不能為零");
    return x / y;
}

static void Main()
{
    double a = 98, b = 0, result = 0;
    
    try
    {
        result = SafeDivision(a, b);
        Console.WriteLine($"{a} / {b} = {result}");
    }
    catch (DivideByZeroException ex)
    {
        Console.WriteLine("嘗試除以零:" + ex.Message);
    }
    finally
    {
        Console.WriteLine("運算結束"); // 永遠執行
    }
}

異常傳播與呼叫堆疊展開

當方法拋出異常且未在當前 try 區塊捕獲時,CLR 會展開呼叫堆疊,尋找最近的匹配 catch:

void Level1() { Level2(); }
void Level2() { Level3(); }
void Level3() { throw new InvalidOperationException(); }

try { Level1(); }
catch (InvalidOperationException) { /* 這裡捕獲 */ }

若堆疊頂端仍無匹配 catch,進程終止並顯示未處理異常對話框。

自訂異常類別

[Serializable]
public class BusinessRuleException : Exception
{
    public string RuleCode { get; }
    public object[] Parameters { get; }

    public BusinessRuleException(string ruleCode, string message, params object[] parameters)
        : base(message)
    {
        RuleCode = ruleCode;
        Parameters = parameters;
    }

    // 序列化建構函式(跨 AppDomain/遠端調用必備)
    protected BusinessRuleException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        RuleCode = info.GetString(nameof(RuleCode));
        Parameters = (object[])info.GetValue(nameof(Parameters), typeof(object[]));
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue(nameof(RuleCode), RuleCode);
        info.AddValue(nameof(Parameters), Parameters);
    }
}

// 使用
throw new BusinessRuleException("AGE_001", "年齡必須大於 18 歲,實際值:{0}", 15);

捕獲順序:從具體到一般

try
{
    using var sw = new StreamWriter(@"C:\test\test.txt");
    sw.WriteLine("Hello");
}
catch (FileNotFoundException ex)  // 最具體
{
    Console.WriteLine("檔案不存在:" + ex.FileName);
}
catch (IOException ex)            // 較一般
{
    Console.WriteLine("IO 錯誤:" + ex.Message);
}
catch (Exception ex)              // 最一般(最後兜底)
{
    Console.WriteLine("未預期錯誤:" + ex);
    throw; // 重新拋出,保留堆疊追蹤
}
finally
{
    // 清理資源(using 已自動處理 StreamWriter)
}

finally 區塊:資源確定性釋放

FileStream fs = null;
try
{
    fs = new FileInfo(@"C:\file.txt").OpenWrite();
    fs.WriteByte(0xF);
}
finally
{
    // 即使 try 區塊拋出異常,finally 仍執行
    if (fs != null) fs.Close(); // 釋放檔案鎖定
}

// 現代寫法:using 陳述式(編譯器轉換為 try-finally)
using var fs = new FileInfo(@"C:\file.txt").OpenWrite();
fs.WriteByte(0xF);
// 隱含 finally: fs.Dispose()

異常處理黃金法則

原則說明
僅捕獲可處理的異常捕獲 Exception 後必須 throw 重拋,否則吞沒錯誤
避免控制流程使用異常異常成本高,業務邏輯用 TryParse、TryGetValue、Result<T> 模式
保留堆疊追蹤用 throw; 而非 throw ex;
提供豐富錯誤資訊自訂異常攜帶錯誤碼、參數、關聯物件
非同步正確處理async Task 方法異常在 await 時拋出,async void 僅限事件處理
全域處理ASP.NET Core: UseExceptionHandler、WPF: DispatcherUnhandledException

特殊場景處理

  • 析構函式異常:會中止析構、呼叫基類析構,若無處理則進程終結
  • 靜態建構函式異常:包裝為 TypeInitializationException,原始異常在 InnerException
  • 執行緒池/Task 異常:.NET 4.5+ 預設不終結進程,需 TaskScheduler.UnobservedTaskException 觀測
  • 記憶體不足:OutOfMemoryException 通常不可恢復,僅能記錄並優雅退出

現代模式:Result<T> 替代部分異常

public readonly struct Result<T>
{
    public T Value { get; }
    public Exception Error { get; }
    public bool IsSuccess => Error == null;

    private Result(T value, Exception error) { Value = value; Error = error; }
    public static Result<T> Success(T value) => new(value, null);
    public static Result<T> Failure(Exception error) => new(default, error);
}

// 使用
Result<int> ParseInt(string s) 
    => int.TryParse(s, out var v) ? Result<int>.Success(v) 
                                   : Result<int>.Failure(new FormatException($"非整數:{s}"));
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved