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

C# throw 關鍵字與自訂例外實作:拋出、重新拋出與序列化支援

     編輯:關於C語言

throw 基本用法

// 1. 拋出新例外
throw new ArgumentNullException(nameof(id), "ID 不可為空");

// 2. 重新拋出目前捕獲的例外 (保留堆疊追蹤)
try
{
    RiskyOperation();
}
catch (Exception ex)
{
    LogError(ex);
    throw; // 關鍵:不是 throw ex!
}

// 3. 捕獲後包裝為新例外 (保留 InnerException)
catch (SqlException ex)
{
    throw new DataAccessException("資料庫操作失敗", ex);
}

throw vs throw ex 差異示範

void MethodA()
{
    try { MethodB(); }
    catch (Exception ex)
    {
        Console.WriteLine("MethodA catch 堆疊:");
        Console.WriteLine(ex.StackTrace);
        throw; // 正確:保留 MethodB 完整堆疊
    }
}

void MethodB()
{
    try { MethodC(); }
    catch (Exception ex)
    {
        Console.WriteLine("MethodB catch 堆疊:");
        Console.WriteLine(ex.StackTrace);
        throw ex; // 錯誤:堆疊追蹤從這裡重設,MethodC 資訊遺失
    }
}

void MethodC() => throw new InvalidOperationException("原始錯誤");

完整自訂例外範本

using System;
using System.Runtime.Serialization;

[Serializable]
public class UserEmployeeException : Exception
{
    public string ErrorCode { get; }
    public string UserId { get; }

    // 無參數
    public UserEmployeeException() : base("使用者員工操作異常") { }

    // 訊息
    public UserEmployeeException(string message) : base(message) { }

    // 訊息 + 內部例外
    public UserEmployeeException(string message, Exception inner) 
        : base(message, inner) { }

    // 豐富建構函式
    public UserEmployeeException(string errorCode, string userId, string message)
        : base(message)
    {
        ErrorCode = errorCode;
        UserId = userId;
    }

    // 序列化建構函式 (跨 AppDomain/遠端必須)
    protected UserEmployeeException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        ErrorCode = info.GetString(nameof(ErrorCode));
        UserId = info.GetString(nameof(UserId));
    }

    // 序列化資料輸出
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue(nameof(ErrorCode), ErrorCode);
        info.AddValue(nameof(UserId), UserId);
    }
}

使用範例

class Program
{
    static void Main()
    {
        try
        {
            ProcessEmployee("EMP001");
        }
        catch (UserEmployeeException ex)
        {
            Console.WriteLine($"錯誤代碼:{ex.ErrorCode}");
            Console.WriteLine($"使用者:{ex.UserId}");
            Console.WriteLine($"訊息:{ex.Message}");
            if (ex.InnerException != null)
                Console.WriteLine($"內部:{ex.InnerException.Message}");
        }
    }

    static void ProcessEmployee(string id)
    {
        if (string.IsNullOrEmpty(id))
            throw new UserEmployeeException("E001", "SYSTEM", "員工 ID 不可為空");
        
        if (!EmployeeExists(id))
            throw new UserEmployeeException("E002", id, "員工不存在");
    }

    static bool EmployeeExists(string id) => false;
}

執行輸出

錯誤代碼:E001
使用者:SYSTEM
訊息:員工 ID 不可為空

設計原則總結

  1. 類別命名以 Exception 結尾:UserEmployeeException
  2. 標記 [Serializable]:支援跨應用程式域、遠端呼叫、記錄序列化
  3. 提供四大標準建構函式:無參數、訊息、訊息+內部例外、序列化
  4. 豐富建構函式攜帶業務資料:錯誤代碼、實體 ID、驗證失敗欄位等
  5. 實作 GetObjectData / 序列化建構函式:確保自訂屬性能正確序列化
  6. 重新拋出永遠用 throw;:保留完整堆疊追蹤
  7. 包裝例外時傳入 innerException:建立因果鏈

進階:例外篩選器搭配自訂例外 (C# 6+)

try
{
    ProcessData();
}
catch (UserEmployeeException ex) when (ex.ErrorCode.StartsWith("E00"))
{
    // 僅處理驗證類錯誤
    ShowValidationError(ex);
}
catch (UserEmployeeException ex) when (ex.ErrorCode.StartsWith("E01"))
{
    // 僅處理資料庫類錯誤
    ShowDbError(ex);
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved