"Không bao giờ cứu Exception trong Ruby!"
Có thể bạn đã nghe điều này trước đây. Đó là lời khuyên tốt, nhưng nó khá khó hiểu trừ khi bạn đã biết. Hãy chia nhỏ câu lệnh này và xem ý nghĩa của nó.
Bạn có thể biết rằng trong Ruby, bạn có thể giải cứu các trường hợp ngoại lệ như vậy:
begin
do_something()
rescue => e
puts e # e is an exception object containing info about the error.
end
Và bạn có thể giải quyết các lỗi cụ thể bằng cách cung cấp tên lớp của lỗi.
begin
do_something()
rescue ActiveRecord::RecordNotFound => e
puts e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
end
Mọi loại ngoại lệ trong Ruby chỉ là một lớp. Trong ví dụ trên, ActiveRecord ::RecordNotFound chỉ là tên của một lớp tuân theo các quy ước nhất định.
Điều này rất quan trọng vì khi bạn cứu RecordNotFound
, bạn cũng giải cứu mọi ngoại lệ kế thừa từ nó.
Tại sao bạn không nên cứu Exception
Sự cố khi giải cứu Exception
là nó thực sự giải cứu mọi ngoại lệ kế thừa từ Exception
. Đó là .... tất cả chúng!
Đó là một vấn đề vì có một số ngoại lệ được sử dụng trong nội bộ Ruby. Chúng không liên quan gì đến ứng dụng của bạn và việc nuốt chúng sẽ khiến những điều tồi tệ xảy ra.
Dưới đây là một vài trong số những cái lớn:
-
SignalException ::Ngắt - Nếu bạn giải quyết vấn đề này, bạn không thể thoát ứng dụng của mình bằng cách nhấn Control-c.
-
ScriptError ::SyntaxError - Lỗi cú pháp nuốt có nghĩa là những thứ như
puts("Forgot something)
sẽ thất bại trong âm thầm. -
NoMemoryError - Bạn muốn biết điều gì sẽ xảy ra khi chương trình của bạn tiếp tục chạy sau khi nó sử dụng hết RAM? Tôi cũng vậy.
begin
do_something()
rescue Exception => e
# Don't do this. This will swallow every single exception. Nothing gets past it.
end
Tôi đoán rằng bạn không thực sự muốn nuốt bất kỳ ngoại lệ cấp hệ thống nào. Bạn chỉ muốn bắt tất cả các lỗi cấp ứng dụng của mình. Các trường hợp ngoại lệ gây ra mã CỦA BẠN.
May mắn thay, có một cách dễ dàng để thực hiện điều này.
Giải cứu StandardError Thay thế
Tất cả các ngoại lệ mà bạn nên quan tâm kế thừa từ StandardError
. Đây là những người bạn cũ của chúng tôi:
-
NoMethodError - nâng lên khi bạn cố gắng gọi một phương thức không tồn tại
-
TypeError - gây ra bởi những thứ như
1 + ""
-
RuntimeError - ai có thể quên RuntimeError cũ tốt?
Để giải cứu những lỗi như thế này, bạn sẽ muốn giải cứu StandardError
. Bạn CÓ THỂ làm điều đó bằng cách viết một cái gì đó như sau:
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
Nhưng Ruby đã làm cho nó dễ sử dụng hơn nhiều.
Khi bạn không chỉ định một lớp ngoại lệ nào cả, ruby cho rằng bạn có nghĩa là StandardError. Vì vậy, mã dưới đây giống với mã trên:
begin
do_something()
rescue => e
# This is the same as rescuing StandardError
end
Các trường hợp ngoại lệ tùy chỉnh nên kế thừa từ StandardError
Vậy điều này có ý nghĩa gì đối với bạn nếu bạn đang tạo các ngoại lệ tùy chỉnh của riêng mình?
Nó có nghĩa là bạn phải luôn kế thừa từ StandardError
và KHÔNG BAO GIỜ khỏi Exception
. Kế thừa từ Ngoại lệ là không tốt vì nó phá vỡ hành vi giải cứu mong đợi. Mọi người sẽ nghĩ rằng họ đang giải quyết tất cả các lỗi cấp ứng dụng nhưng của bạn sẽ chỉ vượt qua.
class SomethingBad < StandardError
end
raise SomethingBad
Cây ngoại lệ
Vì các ngoại lệ của Ruby được thực hiện trong một hệ thống phân cấp, nên có thể hữu ích khi xem nó được trình bày. Dưới đây là danh sách các lớp ngoại lệ đi kèm với thư viện chuẩn của Ruby. Đá quý của bên thứ ba như rails sẽ thêm các lớp ngoại lệ bổ sung vào biểu đồ này, nhưng tất cả chúng sẽ kế thừa từ một số lớp trong danh sách này.
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal