Да, 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 на самом деле достаточно мощный, чтобы вам не приходилось ждать, когда первосвященники Microsoft спустятся с горы и милостиво поменяют свой компилятор для вас. В 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
. Но это общий шаблон, который вы также можете использовать в своем собственном коде для реализации любого вида очистки ресурсов (как using
в C #) или транзакций или чего-либо еще, о чем вы могли подумать.
Единственный случай, когда это не работает, если приобретение и выпуск ресурса распределены по разным частям программы. Но если он локализован, как в вашем примере, вы можете легко использовать эти блоки ресурсов.
Кстати, в современном 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
блока.