Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

ความแตกต่างระหว่าง System Exception และ Application Exception ใน C#

Exception Hierarchy ใน .NET

System.Object
  └─ System.Exception
       ├─ System.SystemException (Base สำหรับ System Errors)
       │    ├─ ArgumentException
       │    ├─ NullReferenceException
       │    ├─ IndexOutOfRangeException
       │    ├─ InvalidOperationException
       │    ├─ OutOfMemoryException
       │    └─ StackOverflowException
       │
       └─ System.ApplicationException (Base สำหรับ User-defined Errors)
            └─ (Custom Exceptions ของคุณ)

ความแตกต่างหลัก

ด้านSystemException (System-level)ApplicationException (App-level)
Base ClassSystem.SystemExceptionSystem.ApplicationException
แหล่งที่มาCLR / .NET FrameworkApplication Code (Developer)
ลักษณะ ErrorNon-recoverable, Fatal, InfrastructureRecoverable, Business Logic, Validation
ตัวอย่างOutOfMemory, StackOverflow, NullReference, DB Connection FailedInvalidInput, InsufficientFunds, BusinessRuleViolation
Handlingมักไม่ Catch (Let Crash/Log)Catch และ Handle ด้วย Try-Catch
Customizationไม่ควร Inheritสร้าง Custom Exception ได้
Purposeแจ้ง Bug/Infrastructure Failureแจ้ง Business Logic Error

SystemException - ตัวอย่าง


// CLR Throws These Automatically
void SystemExamples()
{
    // NullReferenceException - CLR Detect
    string s = null;
    Console.WriteLine(s.Length); // Throw NullReferenceException
    
    // IndexOutOfRangeException
    int[] arr = new int[3];
    arr[5] = 10; // Throw IndexOutOfRangeException
    
    // OutOfMemoryException - CLR Detect (Rare)
    // var big = new byte[int.MaxValue];
    
    // InvalidOperationException - Framework Throws
    var list = new List();
    var enumerator = list.GetEnumerator();
    list.Add(1); // Modified during enumeration
    enumerator.MoveNext(); // Throw InvalidOperationException
}

ApplicationException - Custom Exceptions

⚠️ Microsoft แนะนำ: อย่า Inherit จาก ApplicationException
ใช้ Exception ตรงๆ หรือ Base Class ใดๆ ที่เหมาะสม ApplicationException ไม่ได้เพิ่มคุณค่า และถูก Deprecate ใน Spirit

Best Practice: Custom Exception Pattern


// Base Custom Exception
public abstract class AppException : Exception
{
    public string ErrorCode { get; }
    
    protected AppException(string errorCode, string message) : base(message)
    {
        ErrorCode = errorCode;
    }
    
    protected AppException(string errorCode, string message, Exception inner) 
        : base(message, inner) { ErrorCode = errorCode; }
}

// Specific Business Exceptions
public class ValidationException : AppException
{
    public ValidationException(string message) : base("VALIDATION_ERROR", message) { }
    public ValidationException(string field, string message) 
        : base("VALIDATION_ERROR", $"{field}: {message}") { }
}

public class BusinessRuleException : AppException
{
    public BusinessRuleException(string rule, string message) 
        : base("BUSINESS_RULE_VIOLATION", $"Rule '{rule}': {message}") { }
}

public class NotFoundException : AppException
{
    public NotFoundException(string entity, object id) 
        : base("NOT_FOUND", $"{entity} with id '{id}' not found") { }
}

// Usage
public void Transfer(Account from, Account to, decimal amount)
{
    if (amount <= 0)
        throw new ValidationException("Amount", "Must be positive");
    
    if (from.Balance < amount)
        throw new BusinessRuleException("SUFFICIENT_FUNDS", "Insufficient balance");
    
    if (!Accounts.Contains(to.Id))
        throw new NotFoundException("Account", to.Id);
    
    // ...
}

Exception Handling Strategy


try
{
    Transfer(from, to, amount);
}
catch (ValidationException ex)
{
    // 400 Bad Request - Client Error
    Log.Warning(ex, "Validation failed: {ErrorCode}", ex.ErrorCode);
    return BadRequest(new { error = ex.ErrorCode, message = ex.Message });
}
catch (BusinessRuleException ex)
{
    // 409 Conflict / 422 Unprocessable
    Log.Warning(ex, "Business rule violated: {ErrorCode}", ex.ErrorCode);
    return Conflict(new { error = ex.ErrorCode, message = ex.Message });
}
catch (NotFoundException ex)
{
    // 404 Not Found
    Log.Information(ex, "Resource not found: {ErrorCode}", ex.ErrorCode);
    return NotFound(new { error = ex.ErrorCode, message = ex.Message });
}
catch (Exception ex) // SystemException, Unexpected
{
    // 500 Internal Server Error
    Log.Error(ex, "Unexpected error");
    return StatusCode(500, new { error = "INTERNAL_ERROR", message = "An error occurred" });
}

Anti-Patterns

  • ❌ catch (Exception) แล้ว Swallow (Log แค่บางครั้ง)
  • ❌ Throw new Exception("message") (Generic, ไม่มี Type)
  • ❌ ใช้ Exception ควบคุม Flow (Control Flow)
  • ❌ Catch แล้ว Throw ใหม่โดยไม่ Preserve Stack Trace (throw ex แทน throw)

สรุป

  • SystemException: CLR/Framework Throw, Fatal, ไม่ควร Catch เพื่อ Recover
  • Custom Exception (จาก Exception): Business Logic, Recoverable, Catch & Handle ได้
  • ApplicationException: Legacy Base Class, ไม่แนะนำให้ใช้