Các phương pháp hay nhất để quản lý ngoại lệ trong Java hoặc C # [đã đóng]


117

Tôi đang gặp khó khăn trong việc quyết định cách xử lý các ngoại lệ trong ứng dụng của mình.

Phần lớn nếu các vấn đề của tôi với các ngoại lệ xuất phát từ 1) truy cập dữ liệu thông qua một dịch vụ từ xa hoặc 2) giải mã hóa đối tượng JSON. Rất tiếc, tôi không thể đảm bảo thành công cho một trong hai tác vụ này (cắt kết nối mạng, đối tượng JSON không đúng định dạng nằm ngoài tầm kiểm soát của tôi).

Kết quả là, nếu tôi gặp một ngoại lệ, tôi chỉ cần bắt nó trong hàm và trả về FALSE cho người gọi. Logic của tôi là tất cả những gì người gọi thực sự quan tâm là nếu nhiệm vụ thành công, chứ không phải tại sao nó không thành công.

Đây là một số mã mẫu (trong JAVA) của một phương pháp điển hình)

public boolean doSomething(Object p_somthingToDoOn)
{
    boolean result = false;

    try{
        // if dirty object then clean
        doactualStuffOnObject(p_jsonObject);

        //assume success (no exception thrown)
        result = true;
    }
    catch(Exception Ex)
    {
        //don't care about exceptions
        Ex.printStackTrace();
    }
    return result;
}

Tôi nghĩ rằng cách tiếp cận này là tốt, nhưng tôi thực sự tò mò muốn biết các phương pháp hay nhất để quản lý các ngoại lệ là gì (liệu tôi có thực sự nên đánh dấu một ngoại lệ trong suốt quá trình ngăn xếp cuộc gọi không?).

Tóm tắt các câu hỏi chính:

  1. Có ổn không nếu chỉ bắt các ngoại lệ nhưng không làm bong bóng chúng hoặc thông báo chính thức cho hệ thống (thông qua nhật ký hoặc thông báo cho người dùng)?
  2. Có những phương pháp hay nhất nào dành cho các trường hợp ngoại lệ không dẫn đến mọi thứ yêu cầu khối thử / bắt?

Theo dõi / Chỉnh sửa

Cảm ơn tất cả các phản hồi, đã tìm thấy một số nguồn tuyệt vời về quản lý ngoại lệ trực tuyến:

Có vẻ như quản lý ngoại lệ là một trong những thứ thay đổi tùy theo ngữ cảnh. Nhưng quan trọng nhất, người ta phải nhất quán trong cách họ quản lý các ngoại lệ trong hệ thống.

Ngoài ra, hãy chú ý đến sự thối mã do thử / bắt quá nhiều hoặc không đưa ra một ngoại lệ nào đó (một ngoại lệ là cảnh báo hệ thống, những gì khác cần được cảnh báo?).

Ngoài ra, đây là một bình luận khá lựa chọn từ m3rLinEz .

Tôi có xu hướng đồng ý với Anders Hejlsberg và bạn rằng hầu hết những người gọi điện chỉ quan tâm xem hoạt động có thành công hay không.

Từ nhận xét này, nó đưa ra một số câu hỏi cần suy nghĩ khi xử lý các trường hợp ngoại lệ:

  • Điểm ngoại lệ này được ném là gì?
  • Làm thế nào nó có ý nghĩa để xử lý nó?
  • Người gọi có thực sự quan tâm đến ngoại lệ hay họ chỉ quan tâm cuộc gọi có thành công hay không?
  • Việc buộc người gọi quản lý một trường hợp ngoại lệ tiềm năng có được ưu đãi không?
  • Bạn có đang tôn trọng các idoms của ngôn ngữ không?
    • Bạn có thực sự cần trả về cờ thành công như boolean không? Trả về boolean (hoặc int) là một tư duy C hơn là một Java (trong Java, bạn sẽ chỉ xử lý ngoại lệ).
    • Làm theo các cấu trúc quản lý lỗi được liên kết với ngôn ngữ :)!

Mặc dù bài viết từ cộng đồng oracle bằng java, nhưng đó là lời khuyên chung cho một loại ngôn ngữ rất rộng. Bài viết hay, đúng những gì tôi đang tìm kiếm.
Bunny Rabbit

Câu trả lời:


61

Có vẻ kỳ lạ với tôi rằng bạn muốn bắt các ngoại lệ và biến chúng thành mã lỗi. Bạn nghĩ tại sao người gọi thích mã lỗi hơn các ngoại lệ khi mã lỗi sau là mã mặc định trong cả Java và C #?

Đối với câu hỏi của bạn:

  1. Bạn chỉ nên bắt các trường hợp ngoại lệ mà bạn thực sự có thể xử lý. Chỉ bắt các ngoại lệ không phải là điều đúng đắn nên làm trong hầu hết các trường hợp. Có một vài trường hợp ngoại lệ (ví dụ: ngoại lệ ghi nhật ký và sắp xếp giữa các luồng) nhưng ngay cả đối với những trường hợp đó, bạn thường nên ném lại các ngoại lệ.
  2. Bạn chắc chắn không nên có nhiều câu lệnh try / catch trong mã của mình. Một lần nữa, ý tưởng là chỉ bắt các trường hợp ngoại lệ mà bạn có thể xử lý. Bạn có thể bao gồm một trình xử lý ngoại lệ cao nhất để biến bất kỳ ngoại lệ nào không được xử lý thành một thứ gì đó hữu ích cho người dùng cuối nhưng nếu không, bạn không nên cố gắng bắt từng ngoại lệ ở mọi nơi có thể.

Về lý do tại sao ai đó muốn mã lỗi thay vì ngoại lệ ... Tôi luôn nghĩ rằng thật kỳ lạ khi HTTP vẫn sử dụng mã lỗi, mặc dù ứng dụng của tôi đang tạo ra một ngoại lệ. Tại sao HTTP không thể cho phép tôi chuyển ngoại lệ qua nguyên trạng?
Trejkaz

tốt nhất bạn có thể làm là để bọc các mã lỗi của bạn trong một đơn nguyên
lindenrovio

@Trejkaz Bạn không muốn trả lại chi tiết ngoại lệ cho người dùng vì đó là một rủi ro bảo mật. Đây là lý do tại sao máy chủ HTML trả về mã lỗi. Việc trả về thông báo lỗi cũng là vấn đề bản địa hóa và có thể làm cho HTML có kích thước lớn hơn và trả về chậm hơn. Tất cả những điều này là lý do tại sao tôi nghĩ rằng máy chủ HTML trả về mã lỗi.
Didier A.

Tôi nghĩ tốt hơn nên nói: "Bạn chỉ nên nhấn mạnh những trường hợp ngoại lệ mà bạn thực sự có thể xử lý."
Didier A.

@didibus Tại sao bạn nghĩ rằng những lo ngại về bảo mật sẽ tạo ra sự bất tiện trong quá trình phát triển? Tôi chưa bao giờ nói bất cứ điều gì về sản xuất. Đối với bản địa hóa thông báo lỗi, toàn bộ trang web đã có vấn đề đó và mọi người dường như đang đối phó.
Trejkaz

25

Điều này phụ thuộc vào ứng dụng và tình huống. Nếu bạn xây dựng một thành phần thư viện, bạn nên tạo các ngoại lệ, mặc dù chúng nên được bao bọc để phù hợp với ngữ cảnh với thành phần của bạn. Ví dụ: nếu bạn đang xây dựng Cơ sở dữ liệu Xml và giả sử bạn đang sử dụng hệ thống tệp để lưu trữ dữ liệu của mình và bạn đang sử dụng quyền của hệ thống tệp để bảo mật dữ liệu. Bạn sẽ không muốn tạo ra một ngoại lệ FileIOAccessDenied vì điều đó làm rò rỉ quá trình triển khai của bạn. Thay vào đó, bạn sẽ bao bọc ngoại lệ và đưa ra lỗi AccessDenied. Điều này đặc biệt đúng nếu bạn phân phối thành phần cho các bên thứ ba.

Đối với việc nếu nuốt các trường hợp ngoại lệ thì không sao. Điều đó phụ thuộc vào hệ thống của bạn. Nếu ứng dụng của bạn có thể xử lý các trường hợp lỗi và không có lợi ích gì từ việc thông báo cho người dùng tại sao nó không thành công thì hãy tiếp tục, mặc dù tôi thực sự khuyên bạn nên ghi nhật ký lỗi. Tôi luôn cảm thấy khó chịu khi được gọi để giúp khắc phục sự cố và nhận thấy họ đang nuốt ngoại lệ (hoặc thay thế nó và ném một ngoại lệ mới thay thế mà không đặt ngoại lệ bên trong).

Nói chung, tôi sử dụng các quy tắc sau:

  1. Trong các thành phần & thư viện của mình, tôi chỉ bắt được một ngoại lệ nếu tôi định xử lý nó hoặc làm điều gì đó dựa trên nó. Hoặc nếu tôi muốn cung cấp thêm thông tin theo ngữ cảnh trong một trường hợp ngoại lệ.
  2. Tôi sử dụng cách bắt thử chung ở điểm nhập ứng dụng, hoặc mức cao nhất có thể. Nếu một ngoại lệ xảy ra ở đây, tôi chỉ cần ghi lại nó và để nó không thành công. Lý tưởng nhất là các trường hợp ngoại lệ không bao giờ nên đến đây.

Tôi thấy mã sau đây là một mùi:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Mã như thế này không có ích lợi gì và không nên được đưa vào.


@Josh, điểm tốt về việc nuốt các ngoại lệ nhưng tôi tin rằng có một vài trường hợp có thể chấp nhận chỉ đơn giản là nuốt các ngoại lệ. Trong dự án cuối cùng, đoạn mã của bạn đã được bắt buộc, đoạn mã đó có mùi. Lời khuyên của tôi, nếu bạn không thể xử lý ngoại lệ, hãy cố gắng không nuốt nó.
smaclell

Quyết tâm như tôi đã nói, tất cả phụ thuộc vào ứng dụng và ngữ cảnh cụ thể. Có những lúc tôi nuốt phải các ngoại lệ mặc dù nó rất hiếm và tôi không thể nhớ lại lần cuối cùng mình đã làm như thế nào ;-) Có thể là khi tôi đang ghi nhật ký của chính mình, và việc ghi vào nhật ký và nhật ký phụ không thành công.
JoshBerke

Mã phục vụ một điểm: Bạn có thể đặt một điểm ngắt tại "ném".
Rauhotz

3
Điểm yếu, bạn có thể yêu cầu VS phá vỡ bất kỳ ngoại lệ nào được ném ra hoặc bạn có thể thu hẹp nó và chọn một ngoại lệ cụ thể. Trong VS2008 có một menu item dưới debug (Bạn sẽ cần để tùy chỉnh thanh công cụ của bạn để tìm nó) Được gọi là ngoại lệ
JoshBerke

Ví dụ về "mùi mã" có một tác dụng phụ rõ ràng, ngay cả ở dạng đơn giản đó. Nếu // do somethingbao gồm bất kỳ try/finallykhối nào xung quanh điểm ném, các finallykhối sẽ thực thi trước catchkhối. Nếu không có try/catch, ngoại lệ sẽ bay lên trên cùng của ngăn xếp mà không có bất kỳ finallykhối nào được thực thi. Điều này cho phép trình xử lý cấp cao nhất quyết định có thực thi các finallykhối hay không .
Daniel Earwicker

9

Tôi muốn giới thiệu một nguồn tốt khác về chủ đề này. Đó là cuộc phỏng vấn với các nhà phát minh ra C # và Java, Anders Hejlsberg và James Gosling, về chủ đề Ngoại lệ được kiểm tra của Java.

Thất bại và Ngoại lệ

Ngoài ra còn có các tài nguyên tuyệt vời ở cuối trang.

Tôi có xu hướng đồng ý với Anders Hejlsberg và bạn rằng hầu hết những người gọi điện chỉ quan tâm xem hoạt động có thành công hay không.

Bill Venners : Bạn đã đề cập đến các mối quan tâm về khả năng mở rộng và lập phiên bản liên quan đến các ngoại lệ đã kiểm tra. Bạn có thể làm rõ ý của bạn về hai vấn đề đó?

Anders Hejlsberg : Hãy bắt đầu với việc tạo phiên bản, bởi vì các vấn đề khá dễ dàng nhận thấy ở đó. Giả sử tôi tạo một phương thức foo tuyên bố nó ném ra các ngoại lệ A, B và C. Trong phiên bản hai của foo, tôi muốn thêm một loạt các tính năng và bây giờ foo có thể ném ngoại lệ D. Đó là một thay đổi đột phá đối với tôi thêm D vào mệnh đề ném của phương thức đó, vì trình gọi hiện có của phương thức đó gần như chắc chắn sẽ không xử lý ngoại lệ đó.

Việc thêm một ngoại lệ mới vào mệnh đề ném trong một phiên bản mới sẽ phá vỡ mã khách hàng. Nó giống như thêm một phương thức vào một giao diện. Sau khi bạn xuất bản một giao diện, nó dành cho tất cả các mục đích thực tế là không thể thay đổi, vì bất kỳ quá trình triển khai nào của nó đều có thể có các phương thức mà bạn muốn thêm vào trong phiên bản tiếp theo. Vì vậy, bạn phải tạo một giao diện mới để thay thế. Tương tự với các ngoại lệ, bạn sẽ phải tạo một phương thức hoàn toàn mới gọi là foo2 để ném ra nhiều ngoại lệ hơn, hoặc bạn sẽ phải bắt ngoại lệ D trong foo mới và biến đổi D thành A, B hoặc C.

Bill Venners : Nhưng dù sao thì bạn cũng không phá mã của họ trong trường hợp đó, ngay cả trong một ngôn ngữ không có ngoại lệ được kiểm tra? Nếu phiên bản foo mới sẽ đưa ra một ngoại lệ mới mà khách hàng nên nghĩ đến việc xử lý, thì mã của họ có bị hỏng chỉ vì thực tế là họ không mong đợi ngoại lệ đó khi họ viết mã không?

Anders Hejlsberg : Không, bởi vì trong nhiều trường hợp, mọi người không quan tâm. Họ sẽ không xử lý bất kỳ trường hợp ngoại lệ nào trong số này. Có một trình xử lý ngoại lệ cấp dưới cùng xung quanh vòng lặp tin nhắn của họ. Trình xử lý đó sẽ đưa ra một hộp thoại cho biết điều gì đã xảy ra và tiếp tục. Các lập trình viên bảo vệ mã của họ bằng cách viết try cuối cùng ở khắp mọi nơi, vì vậy họ sẽ phản hồi chính xác nếu một ngoại lệ xảy ra, nhưng họ thực sự không quan tâm đến việc xử lý các ngoại lệ.

Mệnh đề ném, ít nhất là cách nó được triển khai trong Java, không nhất thiết buộc bạn phải xử lý các ngoại lệ, nhưng nếu bạn không xử lý chúng, nó buộc bạn phải xác nhận chính xác những ngoại lệ nào có thể đi qua. Nó yêu cầu bạn bắt các ngoại lệ đã khai báo hoặc đưa chúng vào mệnh đề ném của riêng bạn. Để giải quyết yêu cầu này, mọi người làm những điều vô lý. Ví dụ: họ trang trí mọi phương thức bằng "ném Ngoại lệ". Điều đó chỉ hoàn toàn đánh bại tính năng và bạn chỉ khiến lập trình viên viết thêm những thứ ngớ ngẩn. Điều đó không giúp ích cho ai cả.

CHỈNH SỬA: Đã thêm nhiều chi tiết hơn về hội nghị


Cảm ơn vì điều đó! Tôi đã cập nhật câu hỏi của mình với thông tin về câu trả lời của bạn!
AtariPete

Có vẻ như ông Hejlsberg đang biện minh cho việc xử lý ngoại lệ Pokemon. Một trong những vấn đề lớn đối với việc thiết kế các ngoại lệ trong Java và C # là có quá nhiều thông tin được mã hóa theo kiểu ngoại lệ, trong khi thông tin nên được lưu trữ trong các trường hợp ngoại lệ không có sẵn theo bất kỳ cách nào nhất quán. Các ngoại lệ sẽ lan truyền lên ngăn xếp cuộc gọi cho đến khi tất cả các điều kiện bất thường được đại diện do đó đã được giải quyết; thật không may, loại ngoại lệ - ngay cả khi được công nhận - không cho biết tình huống có được giải quyết hay không. Nếu một ngoại lệ không được công nhận ...
supercat

... tình hình còn tồi tệ hơn. Nếu mã gọi FetchDatavà nó ném ra một loại ngoại lệ không mong muốn, nó không có cách nào để biết liệu ngoại lệ đó chỉ đơn giản là dữ liệu không có sẵn (trong trường hợp đó, khả năng mã lấy được mà không có nó sẽ "giải quyết" nó), hoặc liệu điều đó có nghĩa là CPU đang cháy và hệ thống nên thực hiện "tắt máy an toàn" ngay từ đầu. Có vẻ như ông Hejlsberg đang gợi ý rằng mã nên giả sử trước đây; có lẽ đó là chiến lược tốt nhất có thể dựa trên các hệ thống phân cấp ngoại lệ hiện có, nhưng dù sao thì nó cũng có vẻ không ổn.
supercat

Tôi đồng ý rằng việc ném Exceptin là vô lý, bởi vì hầu hết mọi thứ đều có thể ném ra một ngoại lệ, bạn chỉ nên cho rằng điều này có thể xảy ra. Nhưng khi bạn chỉ định các ngoại lệ như ném A, B, C, nó chỉ là một chú thích, đối với tôi, nó nên được sử dụng như một khối Comment. Nó giống như nói, này khách hàng của chức năng của tôi, đây là một mẹo, có thể bạn muốn đối phó với A, B, C, vì những lỗi này có thể xảy ra khi sử dụng tôi. Nếu bạn thêm D trong tương lai, nó không phải là một vấn đề lớn nếu nó không được giải quyết, nhưng nó giống như thêm tài liệu mới, Oh bằng cách này, bây giờ nó cũng sẽ hữu ích để bắt D.
Didier A.

8

Các ngoại lệ được kiểm tra là một vấn đề gây tranh cãi nói chung và trong Java nói riêng (sau này tôi sẽ cố gắng tìm một số ví dụ cho những người ủng hộ và phản đối chúng).

Theo quy tắc chung, việc xử lý ngoại lệ phải tuân theo các nguyên tắc này, không theo thứ tự cụ thể:

  • Vì lợi ích của việc bảo trì, hãy luôn ghi nhật ký các ngoại lệ để khi bạn bắt đầu thấy lỗi, nhật ký sẽ hỗ trợ chỉ bạn đến nơi mà lỗi của bạn có thể đã bắt đầu. Đừng bao giờ bỏ đi printStackTrace()hoặc thích nó, rất có thể một trong những người dùng của bạn cuối cùng sẽ nhận được một trong những dấu vết ngăn xếp đó và không có kiến ​​thức chính xác về việc phải làm gì với nó.
  • Hãy nắm bắt những ngoại lệ mà bạn có thể xử lý, và chỉ những ngoại lệ đó và xử lý chúng , đừng chỉ ném chúng lên đống.
  • Luôn luôn bắt một lớp ngoại lệ cụ thể, và nói chung bạn không bao giờ nên bắt loại Exception, bạn rất có thể nuốt phải các ngoại lệ quan trọng khác.
  • Không bao giờ (bao giờ) bắt Errors !! , nghĩa là: Không bao giờ bắt ThrowablesErrors là lớp con của cái sau. Errorlà những vấn đề mà bạn rất có thể sẽ không bao giờ xử lý được (ví dụ: OutOfMemoryhoặc các vấn đề JVM khác)

Về trường hợp cụ thể của bạn, hãy đảm bảo rằng bất kỳ ứng dụng khách nào gọi phương thức của bạn sẽ nhận được giá trị trả về thích hợp. Nếu điều gì đó không thành công, phương thức trả về kiểu boolean có thể trả về false, nhưng hãy đảm bảo rằng những nơi bạn gọi phương thức đó có thể xử lý điều đó.


Các ngoại lệ được kiểm tra không phải là vấn đề trong C # vì nó không có chúng.
cletus

3
Imho, đôi khi rất hay để bắt lỗi: Tôi nhớ lại một ứng dụng Java rất tốn bộ nhớ mà tôi đã viết. Tôi đã bắt OutOfMemory-Ex và hiển thị thông báo cho người dùng rằng anh ta đã hết bộ nhớ, rằng anh ta nên thoát khỏi các chương trình khác và nói với anh ta cách khởi động jvm với nhiều không gian đống được chỉ định. Tôi đoán nó đã giúp.
Lena Schimmel

5

Bạn chỉ nên nắm bắt những trường hợp ngoại lệ mà bạn có thể đối phó. Ví dụ: nếu bạn đang xử lý việc đọc qua mạng và kết nối hết thời gian chờ và bạn gặp một ngoại lệ, bạn có thể thử lại. Tuy nhiên, nếu bạn đang đọc trên mạng và nhận được một ngoại lệ IndexOutOfBounds, bạn thực sự không thể xử lý điều đó vì bạn không (tốt, trong trường hợp này là bạn sẽ không) biết điều gì đã gây ra nó. Nếu bạn định trả về false hoặc -1 hoặc null, hãy đảm bảo rằng nó dành cho các trường hợp ngoại lệ cụ thể. Tôi không muốn một thư viện mà tôi đang sử dụng trả về giá trị false trên mạng được đọc khi ngoại lệ được ném ra là heap hết bộ nhớ.


3

Ngoại lệ là những lỗi không nằm trong quá trình thực thi chương trình bình thường. Tùy thuộc vào những gì chương trình của bạn làm và công dụng của nó (tức là một trình xử lý văn bản so với một màn hình tim), bạn sẽ muốn làm những việc khác nhau khi gặp một ngoại lệ. Tôi đã làm việc với mã sử dụng ngoại lệ như một phần của quá trình thực thi thông thường và nó chắc chắn là một mùi mã.

Ví dụ.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

Mã này làm cho tôi khó chịu. IMO bạn không nên khôi phục từ các trường hợp ngoại lệ trừ khi nó là một chương trình quan trọng. Nếu ngoại lệ ném của bạn thì điều tồi tệ đang xảy ra.


2

Tất cả những điều trên có vẻ hợp lý, và thường nơi làm việc của bạn có thể có chính sách. Tại vị trí của chúng tôi, chúng tôi đã xác định các loại Ngoại lệ: SystemException(bỏ chọn) và ApplicationException(đã kiểm tra).

Chúng tôi đã đồng ý rằng SystemExceptions không thể khôi phục được và sẽ xử lý một lần ở đầu. Để cung cấp bối cảnh hơn nữa, chúng tôi SystemExceptions đang exteneded để cho biết nơi họ xảy ra, ví dụ như RepositoryException, ServiceEceptionvv

ApplicationExceptions có thể có ý nghĩa kinh doanh như InsufficientFundsExceptionvà phải được xử lý bằng mã khách hàng.

Witohut một ví dụ cụ thể, rất khó để nhận xét về việc triển khai của bạn, nhưng tôi sẽ không bao giờ sử dụng mã trả lại, chúng là một vấn đề bảo trì. Bạn có thể nuốt một Ngoại lệ, nhưng bạn cần phải quyết định lý do và luôn ghi lại sự kiện và stacktrace. Cuối cùng, vì phương thức của bạn không có xử lý nào khác nên nó khá thừa (ngoại trừ tính đóng gói?), Vì vậy doactualStuffOnObject(p_jsonObject);có thể trả về một boolean!


1

Sau một số suy nghĩ và xem xét mã của bạn, dường như với tôi rằng bạn chỉ đơn giản là ném lại ngoại lệ dưới dạng boolean. Bạn chỉ có thể để phương thức chuyển ngoại lệ này qua (bạn thậm chí không cần phải bắt nó) và xử lý nó trong trình gọi, vì đó là nơi nó quan trọng. Nếu ngoại lệ sẽ khiến người gọi thử lại chức năng này, thì người gọi phải là người nắm bắt ngoại lệ.

Đôi khi có thể xảy ra trường hợp ngoại lệ bạn gặp phải sẽ không có ý nghĩa đối với người gọi (tức là ngoại lệ mạng), trong trường hợp đó, bạn nên đặt nó trong một ngoại lệ cụ thể của miền.

Mặt khác, nếu ngoại lệ báo hiệu một lỗi không thể khôi phục trong chương trình của bạn (tức là kết quả cuối cùng của ngoại lệ này sẽ là kết thúc chương trình) Cá nhân tôi muốn làm rõ điều đó bằng cách bắt nó và đưa ra một ngoại lệ thời gian chạy.


1

Nếu bạn định sử dụng mẫu mã trong ví dụ của mình, hãy gọi nó là TryDoSomething và chỉ bắt các ngoại lệ cụ thể.

Cũng nên xem xét sử dụng Bộ lọc ngoại lệ khi ghi nhật ký các ngoại lệ cho mục đích chẩn đoán. VB có hỗ trợ ngôn ngữ cho các bộ lọc Ngoại lệ. Liên kết đến blog của Greggm có một triển khai có thể được sử dụng từ C #. Bộ lọc ngoại lệ có các thuộc tính tốt hơn cho khả năng gỡ lỗi khi bắt và đánh lại. Cụ thể, bạn có thể ghi lại sự cố trong bộ lọc và để ngoại lệ tiếp tục lan truyền. Phương pháp đó cho phép đính kèm trình gỡ lỗi JIT (Just in Time) để có toàn bộ ngăn xếp ban đầu. Một quả ném lại cắt bỏ ngăn xếp tại điểm nó được trồng lại.

Các trường hợp mà TryXXXX có ý nghĩa là khi bạn đang gói một hàm của bên thứ ba trong các trường hợp không thực sự đặc biệt hoặc khó kiểm tra đơn giản mà không cần gọi hàm. Một ví dụ sẽ như sau:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

Việc bạn có sử dụng một mẫu như TryXXX hay không là một câu hỏi về phong cách. Câu hỏi về việc bắt tất cả các ngoại lệ và nuốt chúng không phải là một vấn đề về phong cách. Đảm bảo rằng các ngoại lệ không mong đợi được phép phổ biến!


Tôi thích mẫu TryXXX trong .net.
JoshBerke

1

Tôi khuyên bạn nên lấy tín hiệu của mình từ thư viện chuẩn cho ngôn ngữ bạn đang sử dụng. Tôi không thể nói C #, nhưng hãy nhìn vào Java.

Ví dụ java.lang.reflect.Array có một setphương thức tĩnh :

static void set(Object array, int index, Object value);

Cách C sẽ là

static int set(Object array, int index, Object value);

... với giá trị trả về là một chỉ báo thành công. Nhưng bạn không còn ở thế giới C nữa.

Một khi bạn nắm được các ngoại lệ, bạn sẽ thấy rằng nó làm cho mã của bạn đơn giản và rõ ràng hơn, bằng cách di chuyển mã xử lý lỗi ra khỏi logic cốt lõi của bạn. Nhằm mục đích có nhiều câu lệnh trong một trykhối duy nhất .

Như những người khác đã lưu ý - bạn nên càng cụ thể càng tốt về loại ngoại lệ mà bạn bắt gặp.


đây là một nhận xét rất hợp lệ, hãy tôn trọng ngôn ngữ và cách nó truyền thống quản lý các vấn đề như vậy. Đừng mang tư duy C vào thế giới Java.
AtariPete

0

Nếu bạn bắt gặp một Exception và trả về false, nó phải là một ngoại lệ rất cụ thể. Bạn không làm điều đó, bạn đang bắt tất cả chúng và trả về sai. Nếu tôi nhận được MyCarIsOnFireException, tôi muốn biết về nó ngay lập tức! Phần còn lại của các Ngoại lệ tôi có thể không quan tâm. Vì vậy, bạn nên có một đống các trình xử lý Ngoại lệ cho biết "whoa whoa something is wrong here" cho một số trường hợp ngoại lệ (rethrow hoặc bắt và rethrow một ngoại lệ mới giải thích tốt hơn những gì đã xảy ra) và chỉ trả về false cho những người khác.

Nếu đây là một sản phẩm mà bạn sẽ tung ra, bạn nên ghi lại những ngoại lệ đó ở đâu đó, nó sẽ giúp bạn điều chỉnh mọi thứ trong tương lai.

Chỉnh sửa: Đối với câu hỏi gói mọi thứ trong một lần thử / bắt, tôi nghĩ câu trả lời là có. Các trường hợp ngoại lệ sẽ rất hiếm trong mã của bạn đến mức mã trong khối bắt thực thi hiếm khi nó hoàn toàn không đạt được hiệu suất. Một ngoại lệ phải là trạng thái mà máy trạng thái của bạn bị hỏng và không biết phải làm gì. Ít nhất là lặp lại một ngoại lệ giải thích những gì đang xảy ra vào thời điểm đó và có ngoại lệ bắt được bên trong nó. "Ngoại lệ trong phương thức doSomeStuff ()" không hữu ích cho những ai phải tìm ra lý do tại sao nó bị hỏng khi bạn đang đi nghỉ (hoặc ở một công việc mới).


Đừng quên rằng các thiết lập của khối ngoại lệ chi phí quá ...
devstuff

0

Chiến lược của tôi:

Nếu hàm ban đầu trả về void tôi thay đổi nó để trả về bool . Nếu ngoại lệ / lỗi xảy ra, trả về false , nếu mọi thứ ổn thì trả về true .

Nếu hàm phải trả về một cái gì đó thì khi ngoại lệ / lỗi xảy ra trả về null , nếu không thì mục có thể trả lại.

Thay vì bool, một chuỗi có thể được trả về chứa mô tả lỗi.

Trong mọi trường hợp trước khi trả về bất cứ điều gì, hãy ghi lại lỗi.


0

Một số câu trả lời xuất sắc ở đây. Tôi muốn nói thêm rằng nếu bạn kết thúc với một cái gì đó như bạn đã đăng, ít nhất hãy in nhiều hơn dấu vết ngăn xếp. Nói những gì bạn đang làm vào thời điểm đó và Ex.getMessage (), để tạo cơ hội chiến đấu cho nhà phát triển.


tôi hoàn toàn đồng ý. Tôi chỉ làm Ex.printStackTrace (); như một ví dụ về điều gì đó mà tôi đang làm trong lần bắt (tức là không ném lại).
AtariPete

0

try / catch khối tạo thành một tập hợp logic thứ hai được nhúng trên tập hợp đầu tiên (chính), vì vậy chúng là một cách tuyệt vời để loại bỏ mã spaghetti khó đọc, khó gỡ lỗi.

Tuy nhiên, được sử dụng hợp lý, chúng có tác dụng tuyệt vời ở khả năng dễ đọc, nhưng bạn chỉ nên tuân theo hai quy tắc đơn giản:

  • sử dụng chúng (một cách tiết kiệm) ở mức thấp để nắm bắt các vấn đề xử lý thư viện và chuyển chúng trở lại luồng logic chính. Hầu hết việc xử lý lỗi mà chúng tôi muốn, phải đến từ chính mã, như một phần của chính dữ liệu. Tại sao phải đưa ra các điều kiện đặc biệt, nếu dữ liệu trả về không đặc biệt?

  • sử dụng một trình xử lý lớn ở cấp cao hơn để quản lý bất kỳ hoặc tất cả các điều kiện kỳ ​​lạ phát sinh trong mã không bị bắt ở cấp thấp. Làm điều gì đó hữu ích với các lỗi (nhật ký, khởi động lại, khôi phục, v.v.).

Ngoài hai loại xử lý lỗi này, tất cả phần còn lại của mã ở giữa phải miễn phí và không có mã try / catch và các đối tượng lỗi. Bằng cách đó, nó hoạt động đơn giản và như mong đợi cho dù bạn sử dụng nó ở đâu hoặc bạn làm gì với nó.

Paul.


0

Tôi có thể hơi muộn với câu trả lời nhưng xử lý lỗi là điều mà chúng tôi luôn có thể thay đổi và phát triển theo thời gian. Nếu bạn muốn đọc thêm điều gì đó về chủ đề này, tôi đã viết một bài đăng trên blog mới của tôi về nó. http://taoofdevelopment.wordpress.com

Chúc bạn viết mã vui vẻ.

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.