Да, 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блока.