Ruby - Ngoại lệ
Việc thực thi và ngoại lệ luôn đi cùng nhau. Nếu bạn đang mở một tệp không tồn tại, thì nếu bạn không xử lý tình huống này đúng cách, thì chương trình của bạn được coi là có chất lượng kém.
Chương trình dừng nếu một ngoại lệ xảy ra. Vì vậy, các ngoại lệ được sử dụng để xử lý các loại lỗi khác nhau, có thể xảy ra trong quá trình thực thi chương trình và thực hiện hành động thích hợp thay vì dừng chương trình hoàn toàn.
Ruby cung cấp một cơ chế tốt để xử lý các ngoại lệ. Chúng tôi đính kèm đoạn mã có thể tạo ra một ngoại lệ trong khối bắt đầu / kết thúc và sử dụng các mệnh đề cứu hộ để cho Ruby biết các loại ngoại lệ mà chúng tôi muốn xử lý.
Cú pháp
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
Mọi thứ từ đầu đến cứu đều được bảo vệ. Nếu một ngoại lệ xảy ra trong quá trình thực thi khối mã này, quyền kiểm soát sẽ được chuyển cho khối giữa cứu hộ và kết thúc .
Đối với mỗi mệnh đề cứu trong khối bắt đầu , Ruby lần lượt so sánh Ngoại lệ được nâng lên với từng tham số. So khớp sẽ thành công nếu ngoại lệ được đặt tên trong mệnh đề cứu giống với loại ngoại lệ hiện đang được ném hoặc là một lớp cha của ngoại lệ đó.
Trong trường hợp ngoại lệ không khớp với bất kỳ loại lỗi nào được chỉ định, chúng tôi được phép sử dụng một mệnh đề khác sau tất cả các mệnh đề cứu .
Thí dụ
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
Điều này sẽ tạo ra kết quả sau. Bạn có thể thấy rằng STDIN được thay thế thành tệp vì không mở được .
#<IO:0xb7d16f84>==#<IO:0xb7d16f84>
Sử dụng câu lệnh thử lại
Bạn có thể nắm bắt một ngoại lệ bằng cách sử dụng khối giải cứu và sau đó sử dụng câu lệnh thử lại để thực thi khối begin từ đầu.
Cú pháp
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
Thí dụ
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
fname = "existant_file"
retry
end
Sau đây là quy trình -
- Đã xảy ra ngoại lệ lúc mở cửa.
- Đã đi giải cứu. fname đã được chỉ định lại.
- Bằng cách thử lại bắt đầu từ đầu.
- Thời gian này tệp mở thành công.
- Tiếp tục quá trình thiết yếu.
NOTE- Lưu ý rằng nếu tệp của tên được thay thế không tồn tại, mã ví dụ này sẽ thử lại vô hạn. Hãy cẩn thận nếu bạn sử dụng thử lại cho một quy trình ngoại lệ.
Sử dụng tuyên bố tăng
Bạn có thể sử dụng tăng tuyên bố để nâng cao một ngoại lệ. Phương thức sau tạo ra một ngoại lệ bất cứ khi nào nó được gọi. Tin nhắn thứ hai sẽ được in.
Cú pháp
raise
OR
raise "Error Message"
OR
raise ExceptionType, "Error Message"
OR
raise ExceptionType, "Error Message" condition
Dạng đầu tiên chỉ cần tăng lại ngoại lệ hiện tại (hoặc RuntimeError nếu không có ngoại lệ hiện tại). Điều này được sử dụng trong các trình xử lý ngoại lệ cần chặn một ngoại lệ trước khi chuyển nó.
Dạng thứ hai tạo một ngoại lệ RuntimeError mới , đặt thông điệp của nó thành chuỗi đã cho. Ngoại lệ này sau đó được nâng lên ngăn xếp cuộc gọi.
Dạng thứ ba sử dụng đối số đầu tiên để tạo một ngoại lệ và sau đó đặt thông báo được liên kết thành đối số thứ hai.
Dạng thứ tư tương tự như dạng thứ ba nhưng bạn có thể thêm bất kỳ câu lệnh điều kiện nào như trừ khi để nêu ra một ngoại lệ.
Thí dụ
#!/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ều này sẽ tạo ra kết quả sau:
I am before the raise.
I am rescued.
I am after the begin block.
Thêm một ví dụ cho thấy việc sử dụng tăng lương -
#!/usr/bin/ruby
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end
Điều này sẽ tạo ra kết quả sau:
A test exception.
["main.rb:4"]
Sử dụng Câu lệnh đảm bảo
Đôi khi, bạn cần đảm bảo rằng một số quá trình xử lý được thực hiện ở cuối khối mã, bất kể có ngoại lệ hay không. Ví dụ: bạn có thể có một tệp đang mở khi nhập vào khối và bạn cần đảm bảo rằng nó được đóng khi khối thoát.
Điều khoản đảm bảo chỉ làm điều này. Đảm bảo đi sau mệnh đề cứu hộ cuối cùng và chứa một đoạn mã sẽ luôn được thực thi khi khối kết thúc. Nó không quan trọng nếu các lối ra khối bình thường, nếu con người dâng và cứu một ngoại lệ, hoặc nếu nó bị chấm dứt bởi một ngoại lệ còn tự do, các đảm bảo khối sẽ được chạy.
Cú pháp
begin
#.. process
#..raise exception
rescue
#.. handle error
ensure
#.. finally ensure execution
#.. This will always execute.
end
Thí dụ
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
ensure
puts "Ensuring execution"
end
Điều này sẽ tạo ra kết quả sau:
A test exception.
["main.rb:4"]
Ensuring execution
Sử dụng câu lệnh khác
Nếu khác khoản hiện diện, nó đi sau khi cứu hộ điều khoản và trước khi bất kỳ đảm bảo .
Phần thân của một mệnh đề else chỉ được thực thi nếu không có ngoại lệ nào được đưa ra bởi phần nội dung chính của mã.
Cú pháp
begin
#.. process
#..raise exception
rescue
# .. handle error
else
#.. executes if there is no exception
ensure
#.. finally ensure execution
#.. This will always execute.
end
Thí dụ
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ều này sẽ tạo ra kết quả sau:
I'm not raising exception
Congratulations-- no errors!
Ensuring execution
Thông báo lỗi đã nâng có thể được ghi lại bằng cách sử dụng $! Biến đổi.
Bắt và ném
Mặc dù cơ chế ngoại lệ của nâng và cứu là tuyệt vời để bỏ thực thi khi có sự cố, đôi khi thật tuyệt khi có thể nhảy ra khỏi một số cấu trúc lồng nhau sâu trong quá trình xử lý bình thường. Đây là nơi mà bắt và ném có ích.
Lệnh bắt xác định một khối được gắn nhãn với tên đã cho (có thể là Biểu tượng hoặc Chuỗi). Khối được thực thi bình thường cho đến khi gặp một lần ném.
Cú pháp
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
Thí dụ
Ví dụ sau sử dụng một cú ném để chấm dứt tương tác với người dùng nếu '!' được nhập để phản hồi lại bất kỳ lời nhắc nào.
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:")
Bạn nên thử chương trình trên trên máy của mình vì nó cần tương tác thủ công. Điều này sẽ tạo ra kết quả sau:
Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby
Ngoại lệ lớp học
Các lớp và mô-đun tiêu chuẩn của Ruby nâng cao các ngoại lệ. Tất cả các lớp ngoại lệ tạo thành một hệ thống phân cấp, với lớp Ngoại lệ ở trên cùng. Cấp độ tiếp theo chứa bảy loại khác nhau -
- Interrupt
- NoMemoryError
- SignalException
- ScriptError
- StandardError
- SystemExit
Có một ngoại lệ khác ở cấp độ này, Fatal, nhưng trình thông dịch Ruby chỉ sử dụng nội bộ này.
Cả ScriptError và StandardError đều có một số lớp con, nhưng chúng ta không cần đi vào chi tiết ở đây. Điều quan trọng là nếu chúng ta tạo các lớp ngoại lệ của riêng mình, chúng cần phải là lớp con của lớp Exception hoặc một trong các lớp con của nó.
Hãy xem một ví dụ -
class FileSaveError < StandardError
attr_reader :reason
def initialize(reason)
@reason = reason
end
end
Bây giờ, hãy xem ví dụ sau, sẽ sử dụng ngoại lệ này:
File.open(path, "w") do |file|
begin
# Write out the data ...
rescue
# Something went wrong!
raise FileSaveError.new($!)
end
end
Dòng quan trọng ở đây là tăng FileSaveError.new ($!) . Chúng tôi gọi raise để báo hiệu rằng một ngoại lệ đã xảy ra, chuyển nó một phiên bản mới của FileSaveError, với lý do là ngoại lệ cụ thể đó đã khiến việc ghi dữ liệu không thành công.