ทับทิม - ข้อยกเว้น

การดำเนินการและข้อยกเว้นไปด้วยกันเสมอ หากคุณกำลังเปิดไฟล์ซึ่งไม่มีอยู่หากคุณไม่ได้จัดการกับสถานการณ์นี้อย่างถูกต้องโปรแกรมของคุณจะถือว่ามีคุณภาพไม่ดี

โปรแกรมจะหยุดทำงานหากมีข้อยกเว้นเกิดขึ้น ดังนั้นจึงมีการใช้ข้อยกเว้นเพื่อจัดการข้อผิดพลาดประเภทต่างๆซึ่งอาจเกิดขึ้นระหว่างการเรียกใช้โปรแกรมและดำเนินการที่เหมาะสมแทนการหยุดโปรแกรมโดยสิ้นเชิง

Ruby เป็นกลไกที่ดีในการจัดการกับข้อยกเว้น เราใส่รหัสที่สามารถเพิ่มข้อยกเว้นในบล็อกเริ่มต้น / สิ้นสุดและใช้คำสั่งช่วยเหลือเพื่อบอก Ruby ถึงประเภทของข้อยกเว้นที่เราต้องการจัดการ

ไวยากรณ์

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

ทุกอย่างตั้งแต่เริ่มต้นจนถึงการช่วยเหลือได้รับการปกป้อง ถ้าข้อยกเว้นเกิดขึ้นระหว่างการดำเนินการบล็อกของรหัสนี้, การควบคุมจะส่งผ่านไปยังบล็อกระหว่างกู้ภัยและสิ้นสุด

สำหรับแต่ละประโยคการช่วยเหลือในบล็อกเริ่มต้น Ruby จะเปรียบเทียบข้อยกเว้นที่เพิ่มขึ้นกับพารามิเตอร์แต่ละตัวในทางกลับกัน การจับคู่จะสำเร็จหากข้อยกเว้นที่ระบุชื่อในส่วนคำสั่งช่วยเหลือเหมือนกับประเภทของข้อยกเว้นที่ถูกโยนในปัจจุบันหรือเป็นคลาสระดับสูงของข้อยกเว้นนั้น

ในกรณีที่ข้อยกเว้นไม่ตรงกับประเภทข้อผิดพลาดใด ๆ ที่ระบุเราได้รับอนุญาตให้ใช้ส่วนคำสั่งอื่นหลังจากคำสั่งการช่วยเหลือทั้งหมด

ตัวอย่าง

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ คุณจะเห็นว่าSTDINถูกแทนที่ด้วยไฟล์เนื่องจากการเปิดล้มเหลว

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

ใช้คำสั่งลองใหม่

คุณสามารถจับข้อยกเว้นโดยใช้บล็อกช่วยเหลือจากนั้นใช้คำสั่งลองใหม่เพื่อดำเนินการบล็อกเริ่มต้นจากจุดเริ่มต้น

ไวยากรณ์

begin
   # Exceptions raised by this code will 
   # be caught by the following rescue clause
rescue
   # This block will capture all types of exceptions
   retry  # This will move control to the beginning of begin
end

ตัวอย่าง

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

ต่อไปนี้เป็นขั้นตอนของกระบวนการ -

  • เกิดข้อยกเว้นเมื่อเปิด
  • ไปกู้ภัย. fname ถูกกำหนดใหม่
  • เมื่อลองอีกครั้งไปที่จุดเริ่มต้นของจุดเริ่มต้น
  • ไฟล์เวลานี้เปิดสำเร็จ
  • ดำเนินกระบวนการที่สำคัญอย่างต่อเนื่อง

NOTE- โปรดสังเกตว่าหากไม่มีไฟล์ของชื่อที่ถูกแทนที่ซ้ำโค้ดตัวอย่างนี้จะลองซ้ำไม่สิ้นสุด โปรดใช้ความระมัดระวังหากคุณใช้การลองใหม่สำหรับกระบวนการยกเว้น

ใช้คำชี้แจงเพิ่ม

คุณสามารถใช้เพิ่มคำสั่งให้เพิ่มข้อยกเว้น วิธีการต่อไปนี้จะทำให้เกิดข้อยกเว้นทุกครั้งที่เรียก จะมีการพิมพ์ข้อความที่สอง

ไวยากรณ์

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

รูปแบบแรกเพียงแค่เพิ่มข้อยกเว้นปัจจุบันอีกครั้ง (หรือ RuntimeError หากไม่มีข้อยกเว้นในปัจจุบัน) ใช้ในตัวจัดการข้อยกเว้นที่จำเป็นต้องสกัดกั้นข้อยกเว้นก่อนส่งต่อ

รูปแบบที่สองสร้างข้อยกเว้นRuntimeErrorใหม่โดยตั้งค่าข้อความเป็นสตริงที่กำหนด จากนั้นข้อยกเว้นนี้จะถูกยกระดับการโทร

รูปแบบที่สามใช้อาร์กิวเมนต์แรกเพื่อสร้างข้อยกเว้นจากนั้นตั้งค่าข้อความที่เกี่ยวข้องเป็นอาร์กิวเมนต์ที่สอง

รูปแบบที่สี่คล้ายกับรูปแบบที่สาม แต่คุณสามารถเพิ่มคำสั่งเงื่อนไขใด ๆ เช่นเว้นแต่จะเพิ่มข้อยกเว้น

ตัวอย่าง

#!/usr/bin/ruby

begin  
   puts 'I am before the raise.'  
   raise 'An error has occurred.'  
   puts 'I am after the raise.'  
rescue  
   puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

I am before the raise.  
I am rescued.  
I am after the begin block.

อีกหนึ่งตัวอย่างที่แสดงให้เห็นถึงการใช้การเพิ่ม -

#!/usr/bin/ruby

begin  
   raise 'A test exception.'  
rescue Exception => e  
   puts e.message  
   puts e.backtrace.inspect  
end

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

A test exception.
["main.rb:4"]

ใช้คำสั่งให้แน่ใจ

บางครั้งคุณต้องรับประกันว่าการประมวลผลบางอย่างเสร็จสิ้นในตอนท้ายของบล็อกโค้ดไม่ว่าจะมีข้อยกเว้นหรือไม่ก็ตาม ตัวอย่างเช่นคุณอาจมีไฟล์ที่เปิดอยู่เมื่อเข้าสู่บล็อกและคุณต้องตรวจสอบให้แน่ใจว่าไฟล์นั้นถูกปิดเมื่อบล็อกออก

ให้แน่ใจว่าข้อไม่เพียงแค่นี้ ตรวจสอบให้แน่ใจว่าดำเนินการหลังจากประโยคการช่วยเหลือสุดท้ายและมีโค้ดส่วนหนึ่งที่จะถูกเรียกใช้งานเสมอเมื่อบล็อกสิ้นสุดลง ไม่สำคัญว่าบล็อกจะออกตามปกติหรือไม่หากเพิ่มและช่วยให้เกิดข้อยกเว้นหรือหากถูกยกเลิกโดยข้อยกเว้นที่ไม่ถูกตรวจจับบล็อกเพื่อให้แน่ใจว่าจะทำงาน

ไวยากรณ์

begin 
   #.. process 
   #..raise exception
rescue 
   #.. handle error 
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

ตัวอย่าง

begin
   raise 'A test exception.'
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
ensure
   puts "Ensuring execution"
end

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

A test exception.
["main.rb:4"]
Ensuring execution

การใช้คำสั่งอื่น

หากอื่นประโยคเป็นปัจจุบันมันจะไปหลังจากที่ช่วยชีวิตข้อและก่อนที่จะให้แน่ใจว่า

เนื้อหาของประโยคอื่นจะดำเนินการเฉพาะในกรณีที่ไม่มีการยกข้อยกเว้นโดยเนื้อหาหลักของรหัส

ไวยากรณ์

begin 
   #.. process 
   #..raise exception
rescue 
   # .. handle error
else
   #.. executes if there is no exception
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

ตัวอย่าง

begin
   # raise 'A test exception.'
   puts "I'm not raising exception"
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
   puts "Ensuring execution"
end

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

ข้อความแสดงข้อผิดพลาดที่เพิ่มขึ้นสามารถบันทึกได้โดยใช้ $! ตัวแปร.

จับและโยน

ในขณะที่กลไกการยกเว้นของการเพิ่มและการช่วยเหลือนั้นยอดเยี่ยมสำหรับการละทิ้งการดำเนินการเมื่อเกิดสิ่งผิดปกติขึ้น แต่บางครั้งก็เป็นเรื่องดีที่สามารถกระโดดออกจากโครงสร้างที่ซ้อนกันลึก ๆ ในระหว่างการประมวลผลปกติ นี่คือที่ที่มีประโยชน์ในการจับและโยน

การจับกำหนดบล็อกที่มีป้ายกำกับด้วยชื่อที่กำหนด (ซึ่งอาจเป็นสัญลักษณ์หรือสตริง) บล็อกจะดำเนินการตามปกติจนกว่าจะพบการโยน

ไวยากรณ์

throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

OR

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

ตัวอย่าง

ตัวอย่างต่อไปนี้ใช้การโยนเพื่อยุติการโต้ตอบกับผู้ใช้หาก "!" ถูกพิมพ์เพื่อตอบสนองต่อการแจ้งเตือนใด ๆ

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

คุณควรลองใช้โปรแกรมข้างต้นบนเครื่องของคุณเนื่องจากต้องใช้การโต้ตอบด้วยตนเอง สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

ข้อยกเว้นของคลาส

คลาสและโมดูลมาตรฐานของ Ruby เพิ่มข้อยกเว้น คลาสข้อยกเว้นทั้งหมดสร้างลำดับชั้นโดยมีคลาส Exception อยู่ด้านบน ระดับถัดไปประกอบด้วยเจ็ดประเภทที่แตกต่างกัน -

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

มีข้อยกเว้นอีกอย่างหนึ่งในระดับนี้ Fatalแต่ล่าม Ruby ใช้สิ่งนี้เป็นการภายในเท่านั้น

ทั้ง ScriptError และ StandardError มีคลาสย่อยจำนวนมาก แต่เราไม่จำเป็นต้องลงรายละเอียดที่นี่ สิ่งสำคัญคือถ้าเราสร้างคลาสข้อยกเว้นของเราเองคลาสเหล่านั้นจะต้องเป็นคลาสย่อยของคลาส Exception หรือคลาสใดคลาสหนึ่ง

ลองดูตัวอย่าง -

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

ตอนนี้ดูตัวอย่างต่อไปนี้ซึ่งจะใช้ข้อยกเว้นนี้ -

File.open(path, "w") do |file|
begin
   # Write out the data ...
rescue
   # Something went wrong!
   raise FileSaveError.new($!)
end
end

สายสำคัญที่นี่คือการเพิ่มFileSaveError.new ($!) เราเรียกการเพิ่มเพื่อส่งสัญญาณว่ามีข้อยกเว้นเกิดขึ้นส่งผ่านอินสแตนซ์ใหม่ของ FileSaveError โดยมีเหตุผลว่าข้อยกเว้นเฉพาะทำให้การเขียนข้อมูลล้มเหลว