Làm thế nào để tránh ném ngoại lệ vexing?


21

Đọc bài viết của Eric Lippert về các trường hợp ngoại lệ chắc chắn là một cái nhìn thoáng qua về cách tôi nên tiếp cận các ngoại lệ, cả với tư cách là nhà sản xuất và người tiêu dùng. Tuy nhiên, tôi vẫn đang cố gắng xác định một hướng dẫn về cách tránh ném ngoại lệ.

Đặc biệt:

  • Giả sử bạn có một phương thức Lưu có thể thất bại vì a) Người khác đã sửa đổi bản ghi trước bạn hoặc b) Giá trị bạn đang cố gắng tạo đã tồn tại . Các điều kiện này được dự kiến ​​và không phải là ngoại lệ, vì vậy thay vì đưa ra một ngoại lệ, bạn quyết định tạo một phiên bản Thử của phương thức của mình, TrySave, trả về một boolean cho biết nếu việc lưu thành công. Nhưng nếu thất bại, làm sao người tiêu dùng biết vấn đề là gì? Hoặc tốt nhất là trả về một enum chỉ ra kết quả, loại Ok / RecordAlperedModified / ValueAlperedExists? Với số nguyên.TryPude vấn đề này không tồn tại, vì chỉ có một lý do mà phương thức có thể thất bại.
  • Là ví dụ trước đây thực sự là một tình huống khó chịu? Hoặc sẽ ném một ngoại lệ trong trường hợp này là cách ưa thích? Tôi biết đó là cách nó được thực hiện trong hầu hết các thư viện và khung, bao gồm cả khung Thực thể.
  • Làm thế nào để bạn quyết định khi nào tạo phiên bản Thử phương thức của mình so với việc cung cấp một số cách để kiểm tra trước xem phương thức đó có hoạt động hay không? Tôi hiện đang làm theo các hướng dẫn sau:
    • Nếu có cơ hội điều kiện cuộc đua, thì hãy tạo phiên bản Thử. Điều này ngăn cản sự cần thiết của người tiêu dùng để bắt một ngoại lệ ngoại sinh. Ví dụ, trong phương thức Save được mô tả trước đây.
    • Nếu phương thức kiểm tra điều kiện khá nhiều sẽ làm tất cả những gì phương thức ban đầu làm, thì hãy tạo một phiên bản Thử. Ví dụ: số nguyên.TryPude ().
    • Trong mọi trường hợp khác, tạo một phương thức để kiểm tra điều kiện.

1
Ví dụ về lưu của bạn có thể thất bại thực sự không phải là một ngoại lệ cực kỳ khó chịu. Nó khá bình thường và có lẽ nên chỉ đơn giản là một ngoại lệ.
S.Lott

@ S.Lott: Ý bạn là gì với nó khá bình thường? Tình hình, hoặc ném một ngoại lệ trong tình huống này? Dù sao, tôi đồng ý với bạn rằng điều đó không rõ ràng nếu thực tế đây là một tình huống bực tức. Tôi sẽ cập nhật câu hỏi.
Mike

"Tình hình, hoặc ném một ngoại lệ trong tình huống này" Cả hai.
S.Lott

Câu trả lời:


24

Giả sử bạn có một phương thức Lưu có thể thất bại vì a) Người khác đã sửa đổi bản ghi trước bạn hoặc b) Giá trị bạn đang cố gắng tạo đã tồn tại. Các điều kiện này được dự kiến ​​và không phải là ngoại lệ, vì vậy thay vì ném một ngoại lệ, bạn quyết định tạo một phiên bản Thử của phương thức của mình, TrySave, trả về một boolean cho biết nếu việc lưu thành công. Nhưng nếu thất bại, làm sao người tiêu dùng biết vấn đề là gì?

Câu hỏi hay.

Câu hỏi đầu tiên xuất hiện trong đầu tôi là: nếu dữ liệu đã có sẵn thì việc lưu lại thất bại theo nghĩa nào? Có vẻ như nó đã thành công với tôi. Nhưng hãy giả sử vì lý lẽ rằng bạn thực sự có nhiều lý do khác nhau tại sao một hoạt động có thể thất bại.

Câu hỏi thứ hai xuất hiện trong đầu tôi là: thông tin bạn muốn trả lại cho người dùng có thể thực hiện được không? Đó là, họ sẽ đưa ra một số quyết định dựa trên thông tin đó?

Khi đèn "check engine" bật sáng, tôi mở mui xe ra, xác minh rằng có một động cơ trong xe của tôi không bốc cháy và mang nó đến gara. Tất nhiên tại nhà để xe họ có tất cả các loại thiết bị chẩn đoán mục đích đặc biệt cho họ biết lý do tại sao đèn báo động cơ được bật, nhưng theo quan điểm của tôi , hệ thống cảnh báo được thiết kế tốt. Tôi không quan tâm vấn đề là do cảm biến oxy đang ghi lại mức oxy bất thường trong buồng đốt, hoặc do đầu dò tốc độ không tải được rút ra, hoặc bất cứ điều gì. Tôi sẽ có hành động tương tự, cụ thể là, để người khác tìm ra điều này .

Người gọi có quan tâm tại sao lưu không thành công? Họ sẽ làm bất cứ điều gì về nó, ngoài việc từ bỏ hoặc thử lại?

Giả sử vì lý do rằng người gọi thực sự sẽ thực hiện các hành động khác nhau tùy thuộc vào lý do tại sao hoạt động thất bại.

Câu hỏi thứ ba xuất hiện trong đầu là: chế độ thất bại có đặc biệt không? Tôi nghĩ rằng bạn có thể nhầm lẫn có thể với ngoại lệ . Tôi sẽ nghĩ về việc hai người dùng cố gắng sửa đổi cùng một bản ghi cùng một lúc là một tình huống đặc biệt nhưng có thể xảy ra, không phải là một tình huống phổ biến.

Chúng ta hãy giả sử vì lý do rằng nó không ngoại lệ.

Câu hỏi thứ tư xuất hiện trong đầu là: có cách nào đáng tin cậy để phát hiện tình huống xấu trước thời hạn không?

Nếu tình huống xấu nằm trong thùng "ngoại sinh" của tôi, thì không. Không có cách nào để nói một cách đáng tin cậy "người dùng khác có sửa đổi bản ghi này không?" bởi vì họ có thể sửa đổi nó sau khi bạn đặt câu hỏi . Câu trả lời là ngay khi nó được sản xuất.

Câu hỏi thứ năm xuất hiện trong đầu là: có cách nào để thiết kế API sao cho tình huống xấu có thể được ngăn chặn?

Ví dụ: bạn có thể thực hiện thao tác "lưu" yêu cầu hai bước. Bước một: có được một khóa trên hồ sơ đang được sửa đổi. Hoạt động đó thành công hoặc thất bại và do đó có thể trả về Boolean. Người gọi sau đó có thể có một chính sách về cách đối phó với thất bại: đợi một lúc và thử lại, từ bỏ, bất cứ điều gì. Bước hai: một khi khóa được lấy, hãy lưu và giải phóng khóa. Bây giờ việc lưu luôn thành công và do đó không cần phải lo lắng về bất kỳ loại xử lý lỗi nào. Nếu tiết kiệm thất bại, đó là thực sự đặc biệt.


Tất cả những điểm rất tốt, cảm ơn. Bây giờ đây là một câu hỏi tu từ có thể tóm tắt bài viết của tôi: Nếu bạn định thiết kế lại File.Open (), bạn sẽ tạo một File.TryOpen () chứ? Làm thế nào bạn sẽ truyền đạt cho người tiêu dùng lý do của sự thất bại? Hay là ném một ngoại lệ ngoại sinh thực sự là sự thỏa hiệp tốt nhất ở đây?
Mike

10
@Mike: Hệ thống tệp là một ví dụ điển hình về việc sử dụng các ngoại lệ ngoại sinh. Họ hiếm khi thất bại, vì vậy thất bại là đặc biệt. Họ thất bại không thể đoán trước và vì những lý do hoàn toàn nằm ngoài sự kiểm soát của người gọi (không có "khóa" nào mà bạn có thể giữ cáp ethernet được cắm) và các lỗi đều đa dạng và có thể thực hiện được (đó là lỗi vì tệp là không tìm thấy so với tập tin được tìm thấy nhưng bạn không có quyền truy cập ghi đều có thể thực hiện được theo các cách khác nhau.) Tất cả đây là những lý do để thể hiện sự thất bại là một ngoại lệ.
Eric Lippert

Tôi thực sự xem xét câu hỏi đã được trả lời ngay bây giờ, nhưng tôi chỉ tò mò;) Nếu phương pháp có thể thất bại vì 2 lý do trở lên, thì thất bại là có thể hành động, những thất bại là không có ngoại lệ và những thất bại có thể được phát hiện trước thời gian cũng không được ngăn chặn, bạn sẽ làm gì?
Mike

@ Giống như tôi không thể nói cho Eric, nhưng đó có vẻ là một nơi tốt cho các mã lỗi. Trở lại một thành viên của một enum, có lẽ.
Matthew Đọc

1

Trong ví dụ của bạn, nếu có thể dễ dàng kiểm tra tình huống ValueAlperedExists, thì cần kiểm tra và có thể đưa ra một ngoại lệ trước khi thử Lưu, tôi không nghĩ nên thử trong trường hợp này. Điều kiện cuộc đua khó kiểm tra trước thời hạn, do đó, gói Save in a Try trong trường hợp này có lẽ là một ý tưởng rất tốt.

Nói chung, nếu có một điều kiện mà tôi nghĩ là rất có thể (chẳng hạn như NoDataReturned, DivideByZero, v.v ...) HOẶC rất dễ kiểm tra (chẳng hạn như bộ sưu tập trống hoặc giá trị NULL), tôi cố gắng kiểm tra nó đi trước thời đại và đối phó với nó trước khi tôi đi đến điểm mà tôi phải bắt một ngoại lệ. Tôi thừa nhận không phải lúc nào cũng dễ dàng biết được các điều kiện này trước thời hạn, đôi khi chúng chỉ xuất hiện khi mã đang được kiểm tra nghiêm ngặt.


0

Các save()phương pháp phải ném một ngoại lệ.

Lớp trên cùng sẽ bắt và thông báo cho người dùng , mà không chấm dứt chương trình, trừ khi đó là một chương trình dòng lệnh giống như Unix, trong trường hợp đó là OK để chấm dứt.

Giá trị trả về không phải là một cách tốt để quản lý ngoại lệ.

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.