Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Ném lại InnerException mà không mất Stack Trace trong C#

Khi catch exception và ném lại, dùng throw; (không có biến) để bảo toàn stack trace gốc. Dùng throw ex; sẽ reset stack trace -> mất thông tin debug.

Sai: throw ex (Mất stack trace)


try
{
    Method1();
}
catch (Exception ex)
{
    throw ex; // SAI: Stack trace bắt đầu từ dòng này
}

Đúng: throw; (Giữ stack trace)


try
{
    Method1();
}
catch (Exception ex)
{
    // Log, wrap, hoặc chỉ rethrow
    throw; // ĐÚNG: Giữ nguyên stack trace từ Method1
}

Ví dụ minh họa


class Program
{
    static void Main()
    {
        try
        {
            Level2();
        }
        catch (Exception ex)
        {
            Console.WriteLine("=== Caught in Main ===");
            Console.WriteLine(ex.StackTrace);
        }
        Console.ReadLine();
    }
    
    static void Level2()
    {
        try
        {
            Level1();
        }
        catch
        {
            throw; // Rethrow đúng cách
        }
    }
    
    static void Level1()
    {
        throw new InvalidOperationException("Error at Level1");
    }
}

Output Stack Trace (giữ nguyên)


=== Caught in Main ===
   at Program.Level1() in ...\Program.cs:line 30
   at Program.Level2() in ...\Program.cs:line 24
   at Program.Main() in ...\Program.cs:line 14

Wrap Exception (Exception Chaining) - Best Practice


try
{
    // Low-level operation
    File.ReadAllText("missing.txt");
}
catch (FileNotFoundException ex)
{
    // Wrap vào exception cấp cao hơn, PRESERVE InnerException
    throw new DataAccessException("Failed to read config file", ex);
    // ex trở thành InnerException, stack trace gốc vẫn giữ nguyên
}

// Custom exception
public class DataAccessException : Exception
{
    public DataAccessException(string message, Exception inner) : base(message, inner) { }
}

Khi nào dùng throw ex?

Hầu như không bao giờ. Chỉ khi bạn muốn ẩn chi tiết implementation (security) và tạo exception mới hoàn toàn, nhưng vẫn nên set InnerException.

Exception Filters (C# 6+) - Không catch để rethrow


// Chỉ catch khi điều kiện true, không cần rethrow
try
{
    Risky();
}
catch (SqlException ex) when (ex.Number == 1205) // Deadlock
{
    // Handle deadlock specifically
    Retry();
}
catch (SqlException ex) when (ex.Number == 2627) // Unique constraint
{
    // Handle duplicate key
    LogDuplicate();
}
// Other SqlException -> bubble up automatically (no rethrow needed)

Tóm tắt

  • throw; -> Giữ stack trace (✓).
  • throw ex; -> Reset stack trace (✗).
  • throw new MyException("msg", ex); -> Wrap, giữ InnerException (✓).
  • when filter -> Tránh catch/rethrow không cần thiết (✓).