"อย่าช่วยชีวิต Exception ใน Ruby!"
บางทีคุณอาจเคยได้ยินเรื่องนี้มาก่อน เป็นคำแนะนำที่ดี แต่ค่อนข้างสับสนเว้นแต่คุณจะรู้อยู่แล้ว เรามาทำลายคำกล่าวนี้กันและดูว่ามันหมายถึงอะไร
คุณคงรู้ว่าใน Ruby คุณสามารถกู้คืนข้อยกเว้นได้ดังนี้:
begin
do_something()
rescue => e
puts e # e is an exception object containing info about the error.
end
และคุณสามารถกู้คืนข้อผิดพลาดบางอย่างได้โดยระบุชื่อคลาสของข้อผิดพลาด
begin
do_something()
rescue ActiveRecord::RecordNotFound => e
puts e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
end
ข้อยกเว้นทุกประเภทใน Ruby เป็นเพียงคลาส ในตัวอย่างข้างต้น ActiveRecord::RecordNotFound เป็นเพียงชื่อของคลาสที่เป็นไปตามข้อตกลงบางประการ
นี่เป็นสิ่งสำคัญเพราะเมื่อคุณช่วย RecordNotFound
คุณยังช่วยเหลือข้อยกเว้นใดๆ ที่สืบทอดมาจากมัน
เหตุใดคุณจึงไม่ควรยกเว้นข้อยกเว้น
ปัญหาการช่วยเหลือ Exception
คือมันช่วยทุกข้อยกเว้นที่สืบทอดมาจาก Exception
. ซึ่งก็คือ....ทั้งหมดนั่นเอง!
นั่นเป็นปัญหาเนื่องจากมีข้อยกเว้นบางประการที่ Ruby ใช้ภายใน พวกเขาไม่มีส่วนเกี่ยวข้องกับแอปของคุณ และการกลืนเข้าไปจะทำให้สิ่งเลวร้ายเกิดขึ้น
นี่คือบางส่วนที่สำคัญ:
-
SignalException::Interrupt - หากคุณช่วยเหลือสิ่งนี้ คุณจะไม่สามารถออกจากแอปได้โดยกดปุ่ม control-c
-
ScriptError::SyntaxError - ข้อผิดพลาดทางไวยากรณ์การกลืนหมายความว่าสิ่งต่าง ๆ เช่น
puts("Forgot something)
จะล้มเหลวอย่างเงียบๆ -
NoMemoryError - ต้องการทราบว่าจะเกิดอะไรขึ้นเมื่อโปรแกรมของคุณทำงานต่อไปหลังจากที่ใช้ RAM หมด ฉันก็เหมือนกัน
begin
do_something()
rescue Exception => e
# Don't do this. This will swallow every single exception. Nothing gets past it.
end
ฉันเดาว่าคุณคงไม่อยากกลืนข้อยกเว้นระดับระบบเหล่านี้ คุณต้องการจับข้อผิดพลาดระดับแอปพลิเคชันทั้งหมดของคุณเท่านั้น ข้อยกเว้นทำให้เกิดรหัสของคุณ
โชคดีที่มีวิธีง่ายๆ ในการทำสิ่งนี้
Rescue StandardError แทน
ข้อยกเว้นทั้งหมดที่คุณควรใส่ใจเกี่ยวกับการสืบทอดจาก StandardError
. นี่คือเพื่อนเก่าของเรา:
-
NoMethodError - ยกขึ้นเมื่อคุณพยายามเรียกใช้วิธีการที่ไม่มีอยู่จริง
-
TypeError - เกิดจากสิ่งต่างๆ เช่น
1 + ""
-
ข้อผิดพลาดรันไทม์ - ใครสามารถลืม RuntimeError แบบเก่าที่ดีได้
ในการแก้ไขข้อผิดพลาดเช่นนี้ คุณจะต้องกู้คืน StandardError
. คุณทำได้โดยเขียนสิ่งนี้:
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
แต่ Ruby ทำให้มันใช้งานง่ายขึ้นมาก
เมื่อคุณไม่ระบุคลาสข้อยกเว้นเลย ruby จะถือว่าคุณหมายถึง StandardError ดังนั้นโค้ดด้านล่างจึงเหมือนกับโค้ดด้านบน:
begin
do_something()
rescue => e
# This is the same as rescuing StandardError
end
ข้อยกเว้นที่กำหนดเองควรรับช่วงต่อจาก StandardError
สิ่งนี้มีความหมายต่อคุณอย่างไรหากคุณกำลังสร้างข้อยกเว้นที่กำหนดเอง
หมายความว่าคุณควรรับช่วงต่อจาก StandardError
และไม่เคยจาก Exception
. การสืบทอดจากข้อยกเว้นนั้นไม่ดีเพราะเป็นการทำลายพฤติกรรมที่คาดหวังของการช่วยเหลือ ผู้คนจะคิดว่าพวกเขากำลังช่วยข้อผิดพลาดระดับแอปพลิเคชันทั้งหมด แต่ข้อผิดพลาดของคุณจะดำเนินต่อไป
class SomethingBad < StandardError
end
raise SomethingBad
ต้นไม้ข้อยกเว้น
เนื่องจากข้อยกเว้นของ Ruby ถูกนำมาใช้ในลำดับชั้นของคลาส การดูการจัดวางจึงอาจเป็นประโยชน์ ด้านล่างนี้คือรายการคลาสข้อยกเว้นที่มาพร้อมกับไลบรารีมาตรฐานของ Ruby อัญมณีของบุคคลที่สาม เช่น Rails จะเพิ่มคลาสข้อยกเว้นเพิ่มเติมให้กับแผนภูมินี้ แต่ทั้งหมดจะสืบทอดมาจากบางคลาสในรายการนี้
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