sự khác biệt giữa
try { ... }
catch{ throw }
và
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Bất kể rằng thứ hai hiển thị một tin nhắn?
sự khác biệt giữa
try { ... }
catch{ throw }
và
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Bất kể rằng thứ hai hiển thị một tin nhắn?
Câu trả lời:
throw;
lấy lại ngoại lệ ban đầu và giữ nguyên dấu vết ngăn xếp ban đầu của nó.
throw ex;
ném ngoại lệ ban đầu nhưng đặt lại dấu vết ngăn xếp, phá hủy tất cả thông tin theo dõi ngăn xếp cho đến khi catch
khối của bạn .
throw ex;
throw new Exception(ex.Message);
thậm chí còn tồi tệ hơn Nó tạo ra một Exception
thể hiện hoàn toàn mới , làm mất dấu vết ngăn xếp ban đầu của ngoại lệ, cũng như kiểu của nó. (ví dụ, IOException
).
Ngoài ra, một số trường hợp ngoại lệ giữ thông tin bổ sung (ví dụ ArgumentException.ParamName
:).
throw new Exception(ex.Message);
cũng sẽ phá hủy thông tin này
Trong một số trường hợp nhất định, bạn có thể muốn bọc tất cả các ngoại lệ trong một đối tượng ngoại lệ tùy chỉnh, để bạn có thể cung cấp thông tin bổ sung về những gì mã đang làm khi ngoại lệ được ném.
Để làm điều này, hãy định nghĩa một lớp mới kế thừa Exception
, thêm tất cả bốn hàm tạo ngoại lệ và tùy chọn một hàm tạo InnerException
bổ sung có thông tin bổ sung và ném lớp ngoại lệ mới của bạn, chuyển ex
làm InnerException
tham số . Bằng cách chuyển bản gốc InnerException
, bạn giữ lại tất cả các thuộc tính của ngoại lệ ban đầu, bao gồm cả dấu vết ngăn xếp.
throw new MyCustomException(myMessage, ex);
tất nhiên.
ex.Message
, đó là tồi tệ hơn.
[Serializable()]
.
throw;
số dòng thực tế nơi xảy ra ngoại lệ được thay thế bằng số dòng của throw;
. Làm thế nào để bạn đề nghị xử lý đó? stackoverflow.com/questions/2493779/ từ
Cái đầu tiên bảo tồn stacktrace ban đầu:
try { ... }
catch
{
// Do something.
throw;
}
Thứ hai cho phép bạn thay đổi loại ngoại lệ và / hoặc tin nhắn và dữ liệu khác:
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
Cũng có một cách thứ ba mà bạn vượt qua một ngoại lệ bên trong:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
Tôi khuyên bạn nên sử dụng:
Một điểm khác mà tôi không thấy ai thực hiện:
Nếu bạn không làm bất cứ điều gì trong khối bắt {} của mình, việc thử ... bắt là vô nghĩa. Tôi thấy điều này mọi lúc:
try
{
//Code here
}
catch
{
throw;
}
Hoặc tồi tệ hơn:
try
{
//Code here
}
catch(Exception ex)
{
throw ex;
}
Tệ nhất
try
{
//Code here
}
catch(Exception ex)
{
throw new System.Exception(ex.Message);
}
throw
ném lại ngoại lệ bị bắt, giữ lại dấu vết ngăn xếp, trong khi throw new Exception
mất một số chi tiết của ngoại lệ bị bắt.
Bạn thường sẽ sử dụng throw
chính nó để ghi lại một ngoại lệ mà không xử lý hoàn toàn tại thời điểm đó.
BlackWasp có một bài viết hay có đủ tiêu đề Ném ngoại lệ trong C # .
Ném một Ngoại lệ mới sẽ thổi bay dấu vết ngăn xếp hiện tại.
throw;
sẽ giữ lại dấu vết ngăn xếp ban đầu và hầu như luôn hữu dụng hơn. Ngoại lệ cho quy tắc đó là khi bạn muốn bọc Ngoại lệ trong Ngoại lệ tùy chỉnh của riêng bạn. Sau đó bạn nên làm:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throw
là để suy nghĩ lại một ngoại lệ bị bắt. Điều này có thể hữu ích nếu bạn muốn làm một cái gì đó với ngoại lệ trước khi chuyển nó lên chuỗi cuộc gọi.
Sử dụng throw
mà không có bất kỳ đối số nào bảo tồn ngăn xếp cuộc gọi cho mục đích gỡ lỗi.
Ví dụ thứ hai của bạn sẽ thiết lập lại dấu vết ngăn xếp của ngoại lệ. Việc đầu tiên bảo tồn chính xác nguồn gốc của ngoại lệ. Ngoài ra, bạn đã mở loại ban đầu là chìa khóa để biết điều gì thực sự đã sai ... Nếu loại thứ hai là bắt buộc đối với chức năng - ví dụ: Để thêm thông tin mở rộng hoặc bọc lại bằng loại đặc biệt, chẳng hạn như 'HandlizableException', thì hãy chắc chắn rằng thuộc tính InternalException cũng được đặt!
Sự khác biệt quan trọng nhất là biểu thức thứ hai xóa loại ngoại lệ. Và loại ngoại lệ đóng vai trò quan trọng trong việc bắt ngoại lệ:
public void MyMethod ()
{
// both can throw IOException
try { foo(); } catch { throw; }
try { bar(); } catch(E) {throw new Exception(E.message); }
}
(...)
try {
MyMethod ();
} catch (IOException ex) {
Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
Console.WriteLine ("Other error"); // [2]
}
Nếu foo()
ném IOException
, [1]
bắt khối sẽ bắt ngoại lệ. Nhưng khi bar()
ném IOException
, nó sẽ được chuyển thành Exception
kiến đơn giản sẽ không bị bắt bởi [1]
khối bắt.
ném hoặc ném ex, cả hai đều được sử dụng để ném hoặc lấy lại ngoại lệ, khi bạn chỉ cần đăng nhập thông tin lỗi và không muốn gửi lại bất kỳ thông tin nào cho người gọi, bạn chỉ cần đăng nhập lỗi và bắt. Nhưng trong trường hợp bạn muốn gửi một số thông tin có ý nghĩa về ngoại lệ cho người gọi bạn sử dụng ném hoặc ném ex. Bây giờ sự khác biệt giữa ném và ném ex là ném bảo tồn dấu vết ngăn xếp và các thông tin khác nhưng ném ex tạo ra một đối tượng ngoại lệ mới và do đó dấu vết ngăn xếp ban đầu bị mất. Vì vậy, khi nào chúng ta nên sử dụng ném và ném e, Vẫn còn một vài tình huống mà bạn có thể muốn nghĩ lại một ngoại lệ như để đặt lại thông tin ngăn xếp cuộc gọi. Ví dụ: nếu phương thức nằm trong thư viện và bạn muốn ẩn chi tiết của thư viện khỏi mã gọi, bạn không nhất thiết muốn ngăn xếp cuộc gọi bao gồm thông tin về các phương thức riêng tư trong thư viện. Trong trường hợp đó, bạn có thể bắt ngoại lệ trong các phương thức công khai của thư viện và sau đó thử lại chúng để ngăn xếp cuộc gọi bắt đầu tại các phương thức công khai đó.
Không có câu trả lời nào ở đây cho thấy sự khác biệt, có thể hữu ích cho những người đang đấu tranh để hiểu sự khác biệt. Xem xét mã mẫu này:
using System;
using System.Collections.Generic;
namespace ExceptionDemo
{
class Program
{
static void Main(string[] args)
{
void fail()
{
(null as string).Trim();
}
void bareThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw;
}
}
void rethrow()
{
try
{
fail();
}
catch (Exception e)
{
throw e;
}
}
void innerThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw new Exception("outer", e);
}
}
var cases = new Dictionary<string, Action>()
{
{ "Bare Throw:", bareThrow },
{ "Rethrow", rethrow },
{ "Inner Throw", innerThrow }
};
foreach (var c in cases)
{
Console.WriteLine(c.Key);
Console.WriteLine(new string('-', 40));
try
{
c.Value();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
Tạo ra đầu ra sau:
Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
--- End of inner exception stack trace ---
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Việc ném trần, như được chỉ ra trong các câu trả lời trước, cho thấy rõ cả dòng mã ban đầu không thành công (dòng 12) cũng như hai điểm khác hoạt động trong ngăn xếp cuộc gọi khi xảy ra ngoại lệ (dòng 19 và 64).
Đầu ra của trường hợp ném lại cho thấy tại sao nó là một vấn đề. Khi ngoại lệ được lấy lại như thế này, ngoại lệ sẽ không bao gồm thông tin ngăn xếp ban đầu. Lưu ý rằng chỉ throw e
bao gồm (dòng 35) và điểm ngăn xếp cuộc gọi ngoài cùng (dòng 64) được bao gồm. Sẽ rất khó để theo dõi phương thức fail () là nguồn gốc của vấn đề nếu bạn ném ngoại lệ theo cách này.
Trường hợp cuối cùng (InternalThrow) là công phu nhất và bao gồm nhiều thông tin hơn một trong những điều trên. Vì chúng tôi đang tạo một ngoại lệ mới, chúng tôi có cơ hội thêm thông tin theo ngữ cảnh (thông báo "bên ngoài", ở đây nhưng chúng tôi cũng có thể thêm vào từ điển .Data về ngoại lệ mới) cũng như lưu giữ tất cả thông tin trong bản gốc ngoại lệ (bao gồm các liên kết trợ giúp, từ điển dữ liệu, v.v.).