ทับทิม - ข้อยกเว้น
การดำเนินการและข้อยกเว้นไปด้วยกันเสมอ หากคุณกำลังเปิดไฟล์ซึ่งไม่มีอยู่หากคุณไม่ได้จัดการกับสถานการณ์นี้อย่างถูกต้องโปรแกรมของคุณจะถือว่ามีคุณภาพไม่ดี
โปรแกรมจะหยุดทำงานหากมีข้อยกเว้นเกิดขึ้น ดังนั้นจึงมีการใช้ข้อยกเว้นเพื่อจัดการข้อผิดพลาดประเภทต่างๆซึ่งอาจเกิดขึ้นระหว่างการเรียกใช้โปรแกรมและดำเนินการที่เหมาะสมแทนการหยุดโปรแกรมโดยสิ้นเชิง
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 โดยมีเหตุผลว่าข้อยกเว้นเฉพาะทำให้การเขียนข้อมูลล้มเหลว