Có sự khác biệt nào giữa ném ném ném và ném ném ra không?


437

Có một số bài viết hỏi sự khác biệt giữa hai cái đó là gì.
(tại sao tôi phải đề cập đến điều này ...)

Nhưng câu hỏi của tôi là khác nhau theo cách mà tôi đang gọi là "throw ex" trong một phương thức xử lý lỗi giống như thần khác .

public class Program {
    public static void Main(string[] args) {
        try {
            // something
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

Nếu try & catchđược sử dụng trong Main, thì tôi sẽ sử dụng throw;để báo cáo lại lỗi. Nhưng trong mã đơn giản ở trên, tất cả các ngoại lệ đều đi quaHandleException

throw ex;tác dụng tương tự như gọi throwkhi được gọi bên trong HandleException?


3
Có một sự khác biệt, nó liên quan đến việc liệu dấu vết ngăn xếp xuất hiện trong trường hợp ngoại lệ hay không, nhưng tôi không nhớ đó là cái gì ngay bây giờ vì vậy tôi sẽ không liệt kê đây là câu trả lời.
Joel Coehoorn

@Joel: Cảm ơn. Tôi đoán sử dụng ngoại lệ HandleError là một ý tưởng tồi. Tôi chỉ muốn cấu trúc lại một số mã xử lý lỗi.
dance2die

1
Cách thứ ba là bọc trong một ngoại lệ mới và rút lại theo thời gian.blogspot.co.uk / 2014/05 / Từ
Tim Abell

Câu trả lời:


679

Có một sự khác biệt;

  • throw exđặt lại theo dõi ngăn xếp (do đó lỗi của bạn sẽ xuất hiện từ HandleException)
  • throw không - người phạm tội ban đầu sẽ được bảo tồn.

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }

28
Để mở rộng câu trả lời của Marc một chút, bạn có thể tìm thêm thông tin chi tiết tại đây: geekswithblogs.net/sdorman/archive 2007/08/20 / Kẻ
Scott Dorman

3
@Shaul; Không, không phải vậy. Tôi đã đưa ra chi tiết trong một bình luận cho bài viết của bạn.
Marc Gravell

1
@Marc Gravell - lời xin lỗi của tôi, bạn đã đúng. Xin lỗi về downvote; đã quá muộn để tôi hoàn tác ... :(
Shaul Behr

3
@Marc: Dường như cú ném chỉ bảo vệ người phạm tội ban đầu nếu cú ​​ném không nằm trong phương thức ném ngoại lệ ban đầu (xem câu hỏi này: stackoverflow.com/questions/5152265/iêu )
Brann

3
@ScottDorman Có vẻ như liên kết của bạn không được chuyển tiếp chính xác sau khi di chuyển blog. Hình như bây giờ nó sống ở đây . Chỉnh sửa: Này, đợi đã, đó là blog của bạn ! Sửa các liên kết của riêng bạn! ; ^ D
ruffin

96

(Tôi đã đăng trước đó và @Marc Gravell đã sửa lỗi cho tôi)

Đây là một minh chứng cho sự khác biệt:

static void Main(string[] args) {
    try {
        ThrowException1(); // line 19
    } catch (Exception x) {
        Console.WriteLine("Exception 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // line 25
    } catch (Exception x) {
        Console.WriteLine("Exception 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // line 34
    } catch {
        throw; // line 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // line 41
    } catch (Exception ex) {
        throw ex; // line 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // line 49
}

và đây là đầu ra:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

Bạn có thể thấy rằng trong Ngoại lệ 1, dấu vết ngăn xếp quay trở lại DivByZero()phương thức, trong khi trong Ngoại lệ 2 thì không.

Tuy nhiên, hãy lưu ý rằng số dòng được hiển thị trong ThrowException1()ThrowException2()là số dòng của throwcâu lệnh, không phải số dòng của cuộc gọi đến DivByZero(), điều này có thể có ý nghĩa bây giờ khi tôi nghĩ về nó một chút ...

Đầu ra trong chế độ phát hành

Ngoại lệ 1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

Ngoại lệ 2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

Có phải nó chỉ duy trì stackTrace ban đầu trong chế độ gỡ lỗi?


1
Đó là do quá trình tối ưu hóa của trình biên dịch đưa ra các phương thức ngắn như DevideByZero, vì vậy dấu vết ngăn xếp là như nhau. có lẽ bạn nên đăng câu hỏi này dưới dạng câu hỏi của riêng mình
Menahem

42

Các câu trả lời khác là hoàn toàn chính xác, nhưng câu trả lời này cung cấp thêm một số điều bất lợi, tôi nghĩ vậy.

Xem xét ví dụ này:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // line 34
      Div(a, b);   // line 35
      Mult(b, a);  // line 36
      Div(b, a);   // line 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

Nếu bạn bỏ ghi chú throw arithExc;dòng, đầu ra của bạn là:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Chắc chắn, bạn đã mất thông tin về nơi xảy ra ngoại lệ đó. Nếu thay vào đó bạn sử dụng throw;dòng, đây là những gì bạn nhận được:

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Điều này tốt hơn rất nhiều, bởi vì bây giờ bạn thấy rằng đó là Program.Divphương pháp gây ra vấn đề cho bạn. Nhưng vẫn khó để xem vấn đề này xuất phát từ dòng 35 hay dòng 37 trong trykhối.

Nếu bạn sử dụng phương án thứ ba, gói trong một ngoại lệ bên ngoài, bạn sẽ không mất thông tin:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

Đặc biệt bạn có thể thấy rằng đó là dòng 35 dẫn đến vấn đề. Tuy nhiên, điều này đòi hỏi mọi người tìm kiếm InnerExceptionvà cảm thấy hơi gián tiếp khi sử dụng các ngoại lệ bên trong trong các trường hợp đơn giản.

Trong bài đăng trên blog này, họ bảo toàn số dòng (dòng của khối thử) bằng cách gọi (thông qua sự phản chiếu) internalphương thức intance InternalPreserveStackTrace()trên Exceptionđối tượng. Nhưng thật không hay khi sử dụng sự phản chiếu như vậy (.NET Framework có thể thay đổi internalthành viên của họ một ngày nào đó mà không cần cảnh báo).


6

hãy hiểu sự khác biệt giữa ném và ném cũ. Tôi nghe nói rằng trong nhiều cuộc phỏng vấn .net, câu hỏi thường gặp này đang được hỏi.

Chỉ cần đưa ra một cái nhìn tổng quan về hai thuật ngữ này, cả ném và ném ex đều được sử dụng để hiểu nơi xảy ra ngoại lệ. Ném ex viết lại dấu vết ngăn xếp của ngoại lệ không phân biệt nơi thực sự đã bị ném.

Hãy hiểu với một ví dụ.

Hãy hiểu ném đầu tiên.

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

đầu ra ở trên là dưới đây.

hiển thị đầy đủ thứ bậc và tên phương thức trong đó thực sự ngoại lệ đã ném .. đó là M2 -> M2. cùng với số dòng

nhập mô tả hình ảnh ở đây

Thứ hai .. hãy hiểu bằng cách ném ex. Chỉ cần thay thế ném với ném ex trong khối bắt phương thức M2. như sau.

nhập mô tả hình ảnh ở đây

đầu ra của mã ex ex như sau ..

nhập mô tả hình ảnh ở đây

Bạn có thể thấy sự khác biệt trong đầu ra .. throw ex chỉ bỏ qua tất cả các cấu trúc phân cấp trước đó và đặt lại dấu vết ngăn xếp với dòng / phương thức trong đó throw ex được viết.


5

Khi bạn làm như vậy throw ex, ngoại lệ ném đó trở thành "bản gốc". Vì vậy, tất cả dấu vết ngăn xếp trước đó sẽ không ở đó.

Nếu bạn làm như vậy throw, ngoại lệ chỉ đi xuống dòng và bạn sẽ nhận được dấu vết ngăn xếp đầy đủ.


4

Không, điều này sẽ khiến ngoại lệ có dấu vết ngăn xếp khác nhau. Chỉ sử dụng throwkhông có bất kỳ đối tượng ngoại lệ nào trong catchtrình xử lý sẽ không thay đổi dấu vết ngăn xếp.

Bạn có thể muốn trả về một boolean từ HandleException cho dù ngoại lệ sẽ được rút lại hay không.


4

MSDN là viết tắt của :

Khi một ngoại lệ được đưa ra, một phần thông tin mà nó mang theo là dấu vết ngăn xếp. Theo dõi ngăn xếp là một danh sách phân cấp cuộc gọi phương thức bắt đầu bằng phương thức ném ngoại lệ và kết thúc bằng phương thức bắt ngoại lệ. Nếu một ngoại lệ được ném lại bằng cách chỉ định ngoại lệ trong câu lệnh throw, dấu vết ngăn xếp được khởi động lại ở phương thức hiện tại và danh sách các lệnh gọi phương thức giữa phương thức ban đầu đã ném ngoại lệ và phương thức hiện tại bị mất. Để giữ thông tin theo dõi ngăn xếp ban đầu với ngoại lệ, hãy sử dụng câu lệnh ném mà không chỉ định ngoại lệ.


2

Nhìn vào đây: http : //blog-mstĩ.blogspot.de/2010/06/throw-vs-throw-ex.html

Ném :

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

Nó lưu giữ thông tin Stack với Exception

Điều này được gọi là "Suy nghĩ lại"

Nếu muốn ném ngoại lệ mới,

throw new ApplicationException("operation failed!");

Ném Ex :

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

Nó sẽ không gửi thông tin ngăn xếp với ngoại lệ

Điều này được gọi là "Phá vỡ ngăn xếp"

Nếu muốn ném ngoại lệ mới,

throw new ApplicationException("operation failed!",ex);

0

Để cung cấp cho bạn một góc nhìn khác về vấn đề này, sử dụng throw đặc biệt hữu ích nếu bạn đang cung cấp API cho khách hàng và bạn muốn cung cấp thông tin theo dõi ngăn xếp dài dòng cho thư viện nội bộ của mình. Bằng cách sử dụng throw here, tôi sẽ nhận được dấu vết ngăn xếp trong trường hợp này của thư viện System.IO.File cho File.Delete. Nếu tôi sử dụng throw ex, thì thông tin đó sẽ không được chuyển đến bộ xử lý của tôi.

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}

-1
int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. nếu tất cả Dòng 1, 2 và 3 được nhận xét - Đầu ra - ex ex

  2. nếu tất cả Dòng 2 và 3 được nhận xét - Đầu ra - bên trong System.DevideByZeroException: {"Đã cố chia cho số không."} ---------

  3. nếu tất cả Dòng 1 và 2 được nhận xét - Đầu ra - ex ex System.Exception: chia cho 0 ----

  4. nếu tất cả Dòng 1 và 3 được nhận xét - Đầu ra - bên trong System.DevideByZeroException: {"Đã cố chia cho số không."} ---------

và StackTrace sẽ được đặt lại trong trường hợp ném ex;

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.