Так, ensure
гарантує, що код завжди оцінюється. Ось чому його називають ensure
. Отже, він еквівалентний Java і C # finally
.
Загальний потік begin
/ rescue
/ else
/ ensure
/ end
виглядає наступним чином :
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
# does not change the final value of the block
end
Ви можете залишити rescue
, ensure
або else
. Ви також можете залишити змінні, і в цьому випадку ви не зможете перевірити виняток у коді обробки винятків. (Ну, ви завжди можете використовувати глобальну змінну винятків, щоб отримати доступ до останнього винятку, який був піднятий, але це трохи хакітно.) І ви можете залишити клас винятку, і в цьому випадку будуть вилучені всі винятки, які успадковуються StandardError
. (Будь ласка , зверніть увагу , що це не означає , що всі виключення спіймані, тому що є винятки , які є екземплярами , Exception
але не StandardError
. В основному дуже серйозні виключення , які ставлять під загрозу цілісність програми , такі як SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
,SignalException
або SystemExit
.)
Деякі блоки утворюють неявні блоки виключень. Наприклад, визначення методів неявно також є блоками виключень, тому замість написання
def foo
begin
# ...
rescue
# ...
end
end
ти пишеш просто
def foo
# ...
rescue
# ...
end
або
def foo
# ...
ensure
# ...
end
Те саме стосується class
визначень та module
визначень.
Однак у конкретному випадку, про який ви запитуєте, насправді є набагато краща ідіома. Взагалі, коли ви працюєте з деяким ресурсом, який вам потрібно очистити наприкінці, ви робите це, передаючи блок методу, який робить усе очищення для вас. Це схоже на using
блок в C #, за винятком того, що Ruby насправді досить потужний, що вам не доведеться чекати, коли первосвященики Майкрософт зійдуть з гори і люб'язно поміняють компілятор для вас. У Ruby ви можете просто реалізувати його самостійно:
# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
file.puts content
end
# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
yield filehandle = new(filename, mode, perm, opt)
ensure
filehandle&.close
end
І що ви знаєте: це вже доступно в основній бібліотеці як File.open
. Але це загальна модель, яку ви також можете використовувати у власному коді для здійснення будь-якого способу очищення ресурсів (à la using
в C #) або транзакцій або будь-якого іншого, про що ви могли б подумати.
Єдиний випадок, коли це не працює, якщо придбання та звільнення ресурсу розподіляються по різних частинах програми. Але якщо він локалізований, як у вашому прикладі, то ви можете легко використовувати ці блоки ресурсів.
BTW: в сучасному C # using
насправді зайве, адже ви можете реалізувати блоки ресурсів у стилі Ruby самостійно:
class File
{
static T open<T>(string filename, string mode, Func<File, T> block)
{
var handle = new File(filename, mode);
try
{
return block(handle);
}
finally
{
handle.Dispose();
}
}
}
// Usage:
File.open("myFile.txt", "w", (file) =>
{
file.WriteLine(contents);
});
begin
блоку.