12/08/2018, 17:08

Ruby - sự khác nhau giữa Exception và StandardError

"Không bao giờ rescue Exception trong Ruby!" Có lẽ bạn đã nghe điều này từ 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 tạm bỏ qua tuyên bố này và xem ý nghĩa của nó. Bạn có thể biết rằng trong Ruby, bạn có thể rescue các ngoại lệ như vậy: begin ...

"Không bao giờ rescue Exception trong Ruby!"

Có lẽ bạn đã nghe điều này từ 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 tạm bỏ qua tuyên bố này và xem ý nghĩa của nó.

Bạn có thể biết rằng trong Ruby, bạn có thể rescue các ngoại lệ như vậy:

begin
  do_something()
rescue => e
  puts e # e là một đối tượng exception, nó chưa các thông tin về lỗi.
end

Và bạn có thể rescue 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 # Chỉ rescues RecordNotFound exceptions, hoặc là các class thừa kế từ RecordNotFound
end

Mỗi loại ngoại lệ trong Ruby chỉ là một class. Trong ví dụ trên, ActiveRecord :: RecordNotFound chỉ là tên của một lớp theo các quy ước nhất định. Điều này rất quan trọng bởi vì khi bạn rescues RecordNotFound, bạn cũng rescue bất kỳ trường hợp ngoại lệ nào được kế thừa từ nó.

Vấn đề với rescues Exception là nó thực sự rescues mọi ngoại lệ được thừa hưởng 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 nội bộ trong Ruby. Chúng không có liên quan gì đến ứng dụng của bạn và nuốt/bắt chúng sẽ gây ra 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 :: Interrupt - Nếu bạn rescues vấn đề này, bạn không thể thoát khỏi ứng dụng bằng cách nhấn control-c.

  • ScriptError :: SyntaxError - Các lỗi cú pháp nuốt/bắt có nghĩa là những thứ như puts ("Quên một cái gì đó") sẽ không thành thục.

  • 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 sử dụng hết RAM?

begin
  do_something()
rescue Exception => e
  # Đừng làm điều này. Điều này sẽ nuốt/bắt mọi ngoại lệ. Không có gì có thể vượt qua được.
end

Tôi đoán rằng bạn không thực sự muốn nuốt/bắt bất kỳ các ngoại lệ cấp hệ thống này. Bạn chỉ muốn bắt tất cả các lỗi mức độ ứng dụng của bạn. Các ngoại lệ gây ra bởi code của BẠN.

May mắn thay, có một cách dễ dàng để làm điều này.

Tất cả các ngoại lệ mà bạn nên quan tâm về kế thừa từ StandardError. Đây là những người bạn cũ của chúng tôi:

  • NoMethodError - được tạo ra khi bạn cố gọi 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?

Để rescue lỗi như thế này, bạn sẽ muốn rescue StandardError. Bạn CÓ THỂ làm điều đó bằng cách viết một cái gì đó như dưới đây:

begin
  do_something()
rescue StandardError => e
  # Chỉ những trường hợp ngoại lệ của ứng dụng của bạn bị nuốt/bắt. Những thứ như SyntaxErrror được để lại một mình.
end

Nhưng Ruby đã làm cho nó dễ dàng hơn nhiều để sử dụng.

Khi bạn không chỉ định một lớp ngoại lệ nào cả, ruby hình thành đối tượng StandardError. Vì vậy, mã dưới đây là giống với mã trên:

begin
  do_something()
rescue => e
  # ở đây giống như rescuing StandardError
end

Vậy điều này có ý nghĩa gì đối với bạn nếu bạn tạo các ngoại lệ tùy chỉnh của riêng bạn?

Nó có nghĩa là bạn nên luôn kế thừa từ StandardError, và KHÔNG BAO GIỜ từ Exception. Thừa kế từ exception là không tốt vì nó phá vỡ các hành vi mong đợi của rescue. Mọi người sẽ nghĩ rằng họ đang rescue tất cả các lỗi ở cấp độ ứng dụng nhưng bạn sẽ chỉ cần thông qua.

class SomethingBad < StandardError
end

raise SomethingBad

Vì các trường hợp ngoại lệ của Ruby được thực hiện trong hệ tầng học, nên có thể nhìn thấy nó được đặt ra. 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. Các gem 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

Đây là bài phân tích đơn giản về cách bắt lỗi, so sánh giữa ưu, nhược điểm và phạm vi hoạt động của Exception và StandardError. Bài dịch và mức độ hiểu của tôi có thể chưa được hoàn thiện, mong bạn đọc thông cảm và góp ý. Xin chân thành cảm ơn!!!

0