Chained Exceptions (ข้อยกเว้นแบบโซ่) ใน C#
Chained Exceptions คือการจับข้อยกเว้น (catch) แล้วโยนข้อยกเว้นใหม่พร้อมเก็บข้อยกเว้นเดิมไว้ใน property InnerException ช่วยให้ติดตามสาเหตุรากฐาน (root cause) ได้ครบถ้วน
โครงสร้าง InnerException
public class Exception
{
public Exception InnerException { get; } // ข้อยกเว้นต้นทาง
public string Message { get; } // ข้อความข้อยกเว้นปัจจุบัน
public string StackTrace { get; } // Stack trace
// ...
}
ตัวอย่าง: สร้าง Chained Exceptions
using System;
class Demo
{
static void Main(string[] args)
{
try
{
Level1();
}
catch (Exception ex)
{
Console.WriteLine("=== Full Exception Chain ===");
PrintExceptionChain(ex);
}
}
static void Level1()
{
try
{
Level2();
}
catch (Exception ex)
{
// โยนข้อยกเว้นใหม่ พร้อมเก็บข้อยกเว้นเดิม
throw new Exception("Level 1: Failed to process", ex);
}
}
static void Level2()
{
try
{
Level3();
}
catch (Exception ex)
{
throw new Exception("Level 2: Database connection failed", ex);
}
}
static void Level3()
{
try
{
Level4();
}
catch (Exception ex)
{
throw new Exception("Level 3: Query execution failed", ex);
}
}
static void Level4()
{
// สาเหตุรากฐาน: จำลองข้อผิดพลาดจริง
throw new InvalidOperationException("Level 4: Connection timeout");
}
static void PrintExceptionChain(Exception ex, int level = 0)
{
string indent = new string(' ', level * 4);
Console.WriteLine($"{indent}Level {level}: {ex.GetType().Name}: {ex.Message}");
if (ex.InnerException != null)
PrintExceptionChain(ex.InnerException, level + 1);
}
}
ผลลัพธ์
=== Full Exception Chain ===
Level 0: Exception: Level 1: Failed to process
Level 1: Exception: Level 2: Database connection failed
Level 2: Exception: Level 3: Query execution failed
Level 3: InvalidOperationException: Level 4: Connection timeout
Best Practices
- ใช้ Exception Filters (C# 6+) เพื่อกรองก่อน rethrow
- อย่าโยน Exception ใหม่โดยไม่มี InnerException จะสูญเสีย context
- ใช้ throw; แทน throw ex; เพื่อรักษา stack trace เดิม
- สร้าง Custom Exception สำหรับ domain-specific errors
// ❌ ผิด - สูญเสีย stack trace
catch (Exception ex) { throw new Exception("Error", ex); }
// ✅ ถูก - เก็บ inner exception
catch (Exception ex) { throw new Exception("Error", ex); }
// ✅ ถูก - rethrow เดิม (preserve stack trace)
catch { throw; }
// ✅ C# 6+ Exception Filter
catch (SqlException ex) when (ex.Number == 1205) // Deadlock
{
// Retry logic
}
Custom Exception แบบ Chained
[Serializable]
public class BusinessException : Exception
{
public string ErrorCode { get; }
public BusinessException(string message, string errorCode, Exception inner = null)
: base(message, inner)
{
ErrorCode = errorCode;
}
}
// ใช้งาน
throw new BusinessException("Transfer failed", "TRANSFER_001", originalEx);
การ Debug: ดู Exception Chain ใน Visual Studio
- Exception Settings (Ctrl+Alt+E) → เลือก Break on thrown
- Watch window:
ex.InnerException,ex.StackTrace ex.ToString()แสดง chain ครบใน Output Window