Ruby QuickRefของ Ryan Davis พูดว่า (ไม่มีคำอธิบาย):
อย่าช่วยให้มีข้อยกเว้น EVER หรือฉันจะแทงคุณ
ทำไมจะไม่ล่ะ? สิ่งที่ถูกต้องทำคืออะไร?
Ruby QuickRefของ Ryan Davis พูดว่า (ไม่มีคำอธิบาย):
อย่าช่วยให้มีข้อยกเว้น EVER หรือฉันจะแทงคุณ
ทำไมจะไม่ล่ะ? สิ่งที่ถูกต้องทำคืออะไร?
คำตอบ:
TL; DR : ใช้StandardErrorแทนการจับข้อยกเว้นทั่วไป เมื่อมีการยกข้อยกเว้นดั้งเดิมขึ้นมาใหม่ (เช่นเมื่อการช่วยชีวิตเพื่อบันทึกข้อยกเว้นเท่านั้น) การช่วยชีวิตExceptionอาจไม่เป็นไร
Exceptionเป็นรากของลำดับชั้นยกเว้นทับทิมดังนั้นเมื่อคุณrescue Exceptionคุณช่วยเหลือจากทุกอย่างรวมทั้ง subclasses เช่นSyntaxError, และLoadErrorInterrupt
การช่วยชีวิตInterruptทำให้ผู้ใช้ไม่CTRLCสามารถออกจากโปรแกรมได้
การช่วยชีวิตSignalExceptionป้องกันไม่ให้โปรแกรมตอบสนองต่อสัญญาณอย่างถูกต้อง มันจะไม่สามารถยกเว้นkill -9ได้
การช่วยชีวิตSyntaxErrorหมายความว่าสิ่งevalที่ล้มเหลวจะทำอย่างเงียบ ๆ
สิ่งเหล่านี้สามารถแสดงได้โดยการเรียกใช้โปรแกรมนี้และลองCTRLCหรือkill:
loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
endการช่วยชีวิตExceptionไม่ได้เป็นค่าเริ่มต้น การทำ
begin
  # iceberg!
rescue
  # lifeboats
endไม่ช่วยเหลือจากมันบ้างจากException StandardErrorโดยทั่วไปคุณควรระบุสิ่งที่เฉพาะเจาะจงมากกว่าค่าเริ่มต้นStandardErrorแต่การช่วยชีวิตจากขอบเขตให้Exception กว้างขึ้นแทนที่จะ จำกัด ให้แคบลงและอาจมีผลร้ายและทำให้การล่าบั๊กยากมาก
หากคุณมีสถานการณ์ที่คุณต้องการช่วยเหลือStandardErrorและคุณต้องการตัวแปรที่มีข้อยกเว้นคุณสามารถใช้แบบฟอร์มนี้:
begin
  # iceberg!
rescue => e
  # lifeboats
endซึ่งเทียบเท่ากับ:
begin
  # iceberg!
rescue StandardError => e
  # lifeboats
endหนึ่งในสองสามกรณีทั่วไปที่มีสติเพื่อช่วยเหลือExceptionคือเพื่อบันทึก / รายงานซึ่งในกรณีนี้คุณควรยกข้อยกเว้นขึ้นใหม่ทันที:
begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise # not enough lifeboats ;)
endThrowableใน java
                    ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error,                             Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]แล้วrescue *ADAPTER_ERRORS => e
                    จริงกฎ: ห้ามทิ้งข้อยกเว้น ความเที่ยงธรรมของผู้เขียนคำพูดของคุณนั้นเป็นที่น่าสงสัยตามที่เห็นได้จากความจริงที่ลงท้ายด้วย
หรือฉันจะแทงคุณ
แน่นอนให้ระวังว่าสัญญาณ (โดยค่าเริ่มต้น) การโยนข้อยกเว้นและกระบวนการที่ใช้เวลานานจะถูกยกเลิกผ่านสัญญาณดังนั้นการจับยกเว้นและไม่ยุติการยกเว้นสัญญาณจะทำให้โปรแกรมของคุณหยุดยาก ดังนั้นอย่าทำสิ่งนี้:
#! /usr/bin/ruby
while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
endไม่ทำไม่ได้จริงๆ อย่าเรียกใช้เพื่อดูว่าใช้งานได้หรือไม่
อย่างไรก็ตามสมมติว่าคุณมีเซิร์ฟเวอร์ที่มีเธรดและคุณต้องการให้ข้อยกเว้นทั้งหมดไม่ใช่:
thread.abort_on_exception = true)  จากนั้นเป็นที่ยอมรับอย่างสมบูรณ์ในเธรดการจัดการการเชื่อมต่อ:
begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
endผลงานด้านบนเป็นรูปแบบของตัวจัดการข้อยกเว้นเริ่มต้นของรูบี้ด้วยข้อดีที่มันไม่ได้ทำให้โปรแกรมของคุณทำงาน Rails ทำสิ่งนี้ในตัวจัดการคำขอ
ข้อยกเว้นสัญญาณถูกยกขึ้นในเธรดหลัก เธรดพื้นหลังจะไม่ได้รับดังนั้นจึงไม่มีประเด็นในการพยายามจับพวกเขาที่นั่น
สิ่งนี้มีประโยชน์อย่างยิ่งในสภาพแวดล้อมการใช้งานจริงซึ่งคุณไม่ต้องการให้โปรแกรมหยุดทำงานเมื่อมีสิ่งผิดปกติเกิดขึ้น จากนั้นคุณสามารถใช้สแต็กดัมพ์ในบันทึกของคุณและเพิ่มลงในโค้ดของคุณเพื่อจัดการกับข้อยกเว้นเฉพาะเพิ่มเติมลงในสายการโทร
โปรดทราบว่ามีสำนวน Ruby อื่นที่มีผลเหมือนกันมาก:
a = do_something rescue "something else"ในบรรทัดนี้ถ้าdo_somethingยกข้อยกเว้นก็จะถูกจับโดยทับทิมโยนออกไปและได้รับมอบหมายa"something else"
โดยทั่วไปแล้วอย่าทำอย่างนั้นยกเว้นในกรณีพิเศษที่คุณรู้ว่าคุณไม่ต้องกังวล ตัวอย่างหนึ่ง:
debugger rescue nildebuggerฟังก์ชั่นเป็นวิธีที่ค่อนข้างดีในการตั้งเบรกพอยต์ในรหัสของคุณ แต่ถ้าทำงานนอกดีบักและทางรถไฟมันจะเพิ่มข้อยกเว้น ทีนี้ในทางทฤษฎีคุณไม่ควรปล่อยให้ debug code วางอยู่ในโปรแกรมของคุณ (pff! ไม่มีใครทำอย่างนั้น!) แต่คุณอาจต้องการเก็บมันไว้ชั่วขณะหนึ่งด้วยเหตุผลบางอย่าง แต่ไม่สามารถเรียกใช้ดีบักเกอร์ของคุณได้
บันทึก:
หากคุณเรียกใช้โปรแกรมของบุคคลอื่นที่จับสัญญาณข้อยกเว้นและละเว้นพวกเขา (พูดรหัสด้านบน) จากนั้น:
pgrep rubyหรือps | grep rubyดูสำหรับโปรแกรมการกระทำผิดกฎหมายของ PID kill -9 <PID>และเรียกใช้แล้ว   หากคุณกำลังทำงานกับโปรแกรมของคนอื่นซึ่งไม่ว่าด้วยเหตุผลใดก็ตามที่มีการใช้บล็อกที่ไม่ใช้ข้อยกเว้นเหล่านี้ให้วางที่ด้านบนสุดของการฉีดยาเป็นวิธีแก้ปัญหาหนึ่งที่เป็นไปได้:
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }นี้ทำให้โปรแกรมที่จะตอบสนองต่อสัญญาณการเลิกจ้างตามปกติโดยทันทียุติผ่านไสข้อยกเว้น ที่ไม่มีการทำความสะอาด ดังนั้นอาจทำให้ข้อมูลสูญหายหรือคล้ายกัน ระวัง!
หากคุณต้องการทำสิ่งนี้:
begin
  do_something
rescue Exception => e
  critical_cleanup
  raise
endคุณสามารถทำสิ่งนี้ได้จริง:
begin
  do_something
ensure
  critical_cleanup
endในกรณีที่สองcritical cleanupจะถูกเรียกทุกครั้งไม่ว่าจะมีข้อยกเว้นหรือไม่
kill -9ได้
                    ensureจะทำงานโดยไม่คำนึงว่ามีข้อยกเว้นเกิดขึ้นหรือไม่ในขณะที่rescueจะทำงานเฉพาะในกรณีที่มีข้อยกเว้นเกิดขึ้น
                    อย่าrescue Exception => e(ยกเว้นการเพิ่มข้อยกเว้นใหม่) - หรือคุณอาจขับรถออกจากสะพาน
สมมติว่าคุณอยู่ในรถ (ใช้ทับทิม) คุณเพิ่งติดตั้งพวงมาลัยใหม่พร้อมระบบอัปเกรดแบบ over-the-air (ซึ่งใช้eval) แต่คุณไม่รู้ว่าโปรแกรมเมอร์คนใดคนหนึ่งสับสนกับไวยากรณ์
คุณอยู่บนสะพานและตระหนักว่าคุณกำลังมุ่งหน้าไปยังทางแยกดังนั้นคุณเลี้ยวซ้าย
def turn_left
  self.turn left:
endโอ๊ะ! นั่นอาจเป็นสิ่งที่ไม่ดี ™โชคดีที่ทับทิมยกตัวSyntaxErrorขึ้น
รถควรหยุดทันที - ใช่ไหม?
Nope
begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
endเสียงบี๊บ
คำเตือน: Caught SyntaxError Exception
ข้อมูล: ข้อผิดพลาดการบันทึก - กระบวนการต่อเนื่อง
คุณสังเกตเห็นบางสิ่งบางอย่างที่ไม่ถูกต้องและคุณได้ชัยชนะในการแบ่งฉุกเฉิน ( ^C: Interrupt)
เสียงบี๊บ
คำเตือน: การดักจับการขัดจังหวะ
ข้อมูล: ข้อผิดพลาดการบันทึก - กระบวนการต่อเนื่อง
ใช่ - นั่นไม่ได้ช่วยอะไรมาก คุณสวยใกล้กับทางรถไฟเพื่อให้คุณใส่รถในสวนสาธารณะ ( killไอเอ็นจี: SignalException)
เสียงบี๊บ
คำเตือน: Caught SignalException Exception
ข้อมูล: ข้อผิดพลาดการบันทึก - กระบวนการต่อเนื่อง
ในวินาทีสุดท้ายคุณดึงกุญแจ ( kill -9) และรถหยุดคุณกระแทกไปที่พวงมาลัย (ถุงลมนิรภัยไม่สามารถพองขึ้นได้เพราะคุณไม่ได้หยุดโปรแกรมอย่างสง่างาม - คุณยกเลิกมัน) และคอมพิวเตอร์ ที่ด้านหลังของรถของคุณกระแทกเข้ากับที่นั่งด้านหน้ามัน กระป๋องโค้กครึ่งกระป๋องหกล้นเหนือกระดาษ ร้านขายของชำด้านหลังถูกบดขยี้และส่วนใหญ่จะอยู่ในไข่แดงและนม รถต้องการการซ่อมแซมและทำความสะอาดที่รุนแรง (การสูญเสียข้อมูล)
หวังว่าคุณจะมีประกัน (สำรอง) โอ้ใช่ - เพราะถุงลมนิรภัยไม่พองตัวคุณอาจบาดเจ็บ (โดนไล่ออก ฯลฯ )
แต่เดี๋ยวก่อน! มีมากกว่าเหตุผลที่คุณอาจต้องการใช้rescue Exception => e!
สมมติว่าคุณเป็นรถคันนั้นและคุณต้องการตรวจสอบให้แน่ใจว่าถุงลมนิรภัยพองตัวหากรถเกินแรงหยุดที่ปลอดภัย
 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
    raise
 endต่อไปนี้เป็นข้อยกเว้น: คุณสามารถจับเท่านั้นถ้าคุณเพิ่มอีกข้อยกเว้นException  ดังนั้นกฎที่ดีกว่าคือไม่เคยกลืนกินExceptionและยกข้อผิดพลาดขึ้นมาใหม่
แต่การเพิ่มการช่วยเหลือนั้นทั้งง่ายที่จะลืมในภาษาเช่น Ruby และใส่คำสั่งการช่วยเหลือทันทีก่อนที่จะหยิบยกปัญหาขึ้นมาใหม่ และคุณไม่ต้องการที่จะลืมraiseคำสั่ง และถ้าคุณทำเช่นนั้นโชคดีที่พยายามค้นหาข้อผิดพลาดนั้น
โชคดีที่ทับทิมยอดเยี่ยมคุณสามารถใช้ensureคำหลักซึ่งทำให้แน่ใจว่ารหัสทำงาน ensureคำหลักที่จะเรียกใช้รหัสไม่ว่าสิ่งที่ - ถ้ายกเว้นจะโยนถ้าใครไม่ได้เป็นข้อยกเว้นเพียงอย่างเดียวถ้าโลกปลาย (หรือเหตุการณ์ที่ไม่น่าที่อื่น ๆ )
 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
 endบูม! และรหัสนั้นควรจะทำงานต่อไป เหตุผลเดียวที่คุณควรใช้rescue Exception => eคือถ้าคุณต้องการเข้าถึงข้อยกเว้นหรือหากคุณต้องการให้โค้ดทำงานบนข้อยกเว้น และอย่าลืมเพิ่มข้อผิดพลาดอีกครั้ง ทุกเวลา.
หมายเหตุ: ตามที่ @Niall ชี้ให้เห็นว่าทำงานอยู่เสมอ นี่เป็นสิ่งที่ดีเพราะบางครั้งโปรแกรมของคุณอาจโกหกคุณและไม่ส่งข้อยกเว้นแม้ว่าจะเกิดปัญหาก็ตาม ด้วยภารกิจที่สำคัญเช่นถุงลมนิรภัยที่พองตัวคุณต้องทำให้แน่ใจว่ามันจะเกิดขึ้นไม่ว่าจะเกิดอะไรขึ้น ด้วยเหตุนี้การตรวจสอบทุกครั้งที่รถหยุดไม่ว่าจะเกิดข้อยกเว้นหรือไม่ก็ตามเป็นความคิดที่ดี ถึงแม้ว่าถุงลมนิรภัยที่พองตัวจะเป็นงานที่ผิดปกติในบริบทการเขียนโปรแกรมส่วนใหญ่ แต่นี่เป็นเรื่องธรรมดาสำหรับงานล้างข้อมูลส่วนใหญ่
ensureทางเลือกที่rescue Exceptionจะทำให้เข้าใจผิด - ตัวอย่างบ่งบอกว่าพวกเขาเทียบเท่า แต่ตามที่ระบุไว้ensureจะเกิดขึ้นไม่ว่าจะมีข้อยกเว้นหรือไม่ดังนั้นตอนนี้ถุงลมนิรภัยของคุณจะพองเพราะคุณไป 5 ไมล์ต่อชั่วโมง
                    เพราะสิ่งนี้จับข้อยกเว้นทั้งหมด ก็ไม่น่าที่โปรแกรมของคุณสามารถกู้คืนจากใด ๆของพวกเขา
คุณควรจัดการข้อยกเว้นเฉพาะที่คุณรู้วิธีการกู้คืนจาก หากคุณไม่คาดว่าจะมีข้อยกเว้นบางประเภทอย่าจัดการมันพังเสียงดัง (เขียนรายละเอียดลงในบันทึก) จากนั้นจึงวิเคราะห์บันทึกและรหัสแก้ไข
การกลืนข้อยกเว้นไม่ดีอย่าทำเช่นนี้
นั่นเป็นกรณีเฉพาะของกฎที่คุณไม่ควรจับใด ๆยกเว้นคุณไม่ทราบวิธีการจัดการ หากคุณไม่ทราบวิธีจัดการกับมันจะดีกว่าเสมอที่จะให้ส่วนอื่น ๆ ของระบบจับและจัดการได้
ฉันเพิ่งอ่านโพสต์บล็อกที่ยอดเยี่ยมเกี่ยวกับมันที่honeybadger.io :
ทำไมคุณไม่ควรช่วยเหลือการยกเว้น
ปัญหาเกี่ยวกับการช่วยเหลือ Exception คือว่ามันช่วยจริงทุกข้อยกเว้นที่สืบทอดจาก Exception นั่นคือ .... พวกเขาทั้งหมด!
นั่นเป็นปัญหาเพราะมีข้อยกเว้นบางอย่างที่ทับทิมใช้ภายใน พวกเขาไม่มีอะไรเกี่ยวข้องกับแอพของคุณและการกลืนพวกมันจะทำให้สิ่งเลวร้ายเกิดขึ้น
นี่คือบางส่วนที่มีขนาดใหญ่:
SignalException :: Interrupt - หากคุณช่วยเหลือสิ่งนี้คุณจะไม่สามารถออกจากแอพได้โดยกดปุ่ม control-c
ScriptError :: SyntaxError - ข้อผิดพลาดทางไวยากรณ์การกลืนหมายความว่าสิ่งต่าง ๆ ที่ทำให้ ("ลืมอะไรบางอย่าง") จะล้มเหลวอย่างเงียบ ๆ
NoMemoryError - อยากรู้ว่าจะเกิดอะไรขึ้นเมื่อโปรแกรมของคุณทำงานต่อไปหลังจากที่ใช้แรมหมดแล้ว? ฉันก็ไม่เหมือนกัน.
begin do_something() rescue Exception => e # Don't do this. This will swallow every single exception. Nothing gets past it. endฉันเดาว่าคุณไม่ต้องการกลืนข้อยกเว้นระดับระบบใด ๆ เหล่านี้ คุณต้องการจับข้อผิดพลาดระดับแอปพลิเคชันทั้งหมดของคุณเท่านั้น ข้อยกเว้นทำให้รหัสของคุณ
โชคดีที่มีวิธีง่ายๆในการทำสิ่งนี้