Câu trả lời:
Các công trình
try { ... }
catch () { ... } /* You can even omit the () here */
try { ... }
catch (Exception e) { ... }
giống nhau ở chỗ cả hai sẽ bắt mọi ngoại lệ được đưa vào bên trong try
khối (và, trừ khi bạn chỉ đơn giản sử dụng điều này để ghi lại các ngoại lệ, nên tránh ). Bây giờ hãy xem những điều này:
try { ... }
catch ()
{
/* ... */
throw;
}
try { ... }
catch (Exception e)
{
/* ... */
throw;
}
try { ... }
catch (Exception e)
{
/* ... */
throw e;
}
Khối try-catch thứ nhất và thứ hai CHÍNH XÁC giống nhau, chúng chỉ đơn giản là ném lại ngoại lệ hiện tại và ngoại lệ đó sẽ giữ nguyên "nguồn" và dấu vết ngăn xếp.
Khối try-catch thứ ba thì khác. Khi nó ném ngoại lệ, nó sẽ thay đổi nguồn và dấu vết ngăn xếp, do đó nó sẽ xuất hiện rằng ngoại lệ đã được ném từ phương thức này, từ chính dòng đó throw e
trên phương thức chứa khối try-catch đó.
Bạn nên sử dụng cái nào? Nó thực sự phụ thuộc vào từng trường hợp.
Giả sử bạn có một Person
lớp với một .Save()
phương thức sẽ lưu nó vào cơ sở dữ liệu. Giả sử rằng ứng dụng của bạn thực thi Person.Save()
phương thức ở đâu đó. Nếu DB của bạn từ chối lưu Người, thì .Save()
sẽ đưa ra một ngoại lệ. Bạn có nên sử dụng throw
hoặcthrow e
trong trường hợp này? Vâng, nó phụ thuộc.
Điều tôi thích là làm:
try {
/* ... */
person.Save();
}
catch(DBException e) {
throw new InvalidPersonException(
"The person has an invalid state and could not be saved!",
e);
}
Điều này sẽ đặt DBException là "Ngoại lệ bên trong" của ngoại lệ mới hơn được ném. Vì vậy, khi bạn kiểm tra InvalidPersonException này, dấu vết ngăn xếp sẽ chứa thông tin quay lại phương thức Lưu (có thể đủ để bạn giải quyết vấn đề), nhưng bạn vẫn có quyền truy cập vào ngoại lệ ban đầu nếu bạn cần.
Nhận xét cuối cùng, khi bạn đang mong đợi một ngoại lệ, bạn thực sự nên nắm bắt một ngoại lệ cụ thể đó chứ không phải một ngoại lệ chung chung Exception
, tức là nếu bạn đang mong đợi một InvalidPersonException, bạn nên thích:
try { ... }
catch (InvalidPersonException e) { ... }
đến
try { ... }
catch (Exception e) { ... }
Chúc may mắn!
Đầu tiên bảo tồn dấu vết ngăn xếp trong khi thứ hai đặt lại nó. Điều này có nghĩa là nếu bạn sử dụng cách tiếp cận thứ hai, dấu vết ngăn xếp của ngoại lệ sẽ luôn bắt đầu từ phương pháp này và bạn sẽ mất dấu vết ngoại lệ ban đầu, điều này có thể gây tai hại cho người đọc nhật ký ngoại lệ vì anh ta sẽ không bao giờ tìm ra nguyên nhân ban đầu của ngoại lệ .
Cách tiếp cận thứ hai có thể hữu ích khi bạn muốn thêm thông tin bổ sung vào dấu vết ngăn xếp nhưng nó được sử dụng như thế này:
try
{
// do something
}
catch (Exception ex)
{
throw new Exception("Additional information...", ex);
}
Có một bài đăng trên blog thảo luận về sự khác biệt.
throw
vs throw e
.
Bạn nên sử dụng
try { }
catch(Exception e)
{ throw }
nếu bạn muốn làm điều gì đó với ngoại lệ trước khi ném lại nó (ghi nhật ký chẳng hạn). Sự cô đơn ném bảo tồn dấu vết ngăn xếp.
Sự khác biệt giữa bắt không tham số và một catch(Exception e)
là bạn nhận được một tham chiếu đến ngoại lệ. Từ phiên bản khung 2, các ngoại lệ không được quản lý được bao bọc trong một ngoại lệ được quản lý, do đó, ngoại lệ không tham số không còn hữu ích cho bất cứ điều gì.
Sự khác biệt giữa throw;
và throw e;
là cái đầu tiên được sử dụng để ném lại các ngoại lệ và cái thứ hai được sử dụng để ném một ngoại lệ mới được tạo. Nếu bạn sử dụng cái thứ hai để ném lại một ngoại lệ, nó sẽ coi nó như một ngoại lệ mới và thay thế tất cả thông tin ngăn xếp từ nơi nó được ném ban đầu.
Vì vậy, bạn không nên sử dụng một trong hai lựa chọn thay thế trong câu hỏi. Bạn không nên sử dụng bắt không có tham số và bạn nên sử dụng throw;
để ném lại một ngoại lệ.
Ngoài ra, trong hầu hết các trường hợp, bạn nên sử dụng một lớp ngoại lệ cụ thể hơn lớp cơ sở cho tất cả các trường hợp ngoại lệ. Bạn chỉ nên nắm bắt những trường hợp ngoại lệ mà bạn dự đoán.
try {
...
} catch (IOException e) {
...
throw;
}
Nếu bạn muốn thêm bất kỳ thông tin nào khi nhập lại ngoại lệ, bạn tạo một ngoại lệ mới với ngoại lệ ban đầu là ngoại lệ bên trong để bảo toàn tất cả thông tin:
try {
...
} catch (IOException e) {
...
throw new ApplicationException("Some informative error message", e);
}