Tak, ensure
zapewnia, że kod jest zawsze analizowany. Dlatego się nazywa ensure
. Jest to więc odpowiednik Javy i C # finally
.
Ogólny przepływ begin
/ rescue
/ else
/ ensure
/ end
wygląda następująco:
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
Można pominąć rescue
, ensure
lub else
. Możesz także pominąć zmienne, w którym to przypadku nie będziesz mógł sprawdzić wyjątku w kodzie obsługi wyjątków. (Cóż, zawsze możesz użyć globalnej zmiennej wyjątku, aby uzyskać dostęp do ostatniego zgłoszonego wyjątku, ale to jest trochę zhackowane.) I możesz pominąć klasę wyjątków, w którym to przypadku StandardError
wychwycone zostaną wszystkie wyjątki, które dziedziczą . (Należy pamiętać, że to nie oznacza, że wszystkie wyjątki zostały złowione, ponieważ istnieją wyjątki, które są przypadki Exception
, ale nie StandardError
. Głównie bardzo poważne wyjątki że kompromis integralność programu, takie jak SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, lubInterrupt
,SignalException
SystemExit
.)
Niektóre bloki tworzą niejawne bloki wyjątków. Na przykład definicje metod są domyślnie także blokami wyjątków, więc zamiast pisania
def foo
begin
# ...
rescue
# ...
end
end
piszesz tylko
def foo
# ...
rescue
# ...
end
lub
def foo
# ...
ensure
# ...
end
To samo dotyczy class
definicji i module
definicji.
Jednak w konkretnym przypadku, o który pytasz, istnieje o wiele lepszy idiom. Ogólnie rzecz biorąc, gdy pracujesz z jakimś zasobem, który musisz oczyścić na końcu, robisz to, przekazując blok metodzie, która wykonuje za Ciebie całe czyszczenie. Jest podobny do using
bloku w języku C #, z tym wyjątkiem, że Ruby jest wystarczająco potężny, abyś nie musiał czekać na najwyższych kapłanów Microsoftu, którzy zejdą z góry i łaskawie zmienią dla ciebie kompilator. W Ruby możesz to po prostu zaimplementować:
# 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
A co wiesz: jest to już dostępne w podstawowej bibliotece jako File.open
. Ale jest to ogólny wzorzec, którego można również użyć we własnym kodzie, do implementacji wszelkiego rodzaju czyszczenia zasobów (à la using
w języku C #) lub transakcji lub cokolwiek innego, co możesz wymyślić.
Jedyny przypadek, w którym to nie działa, jeśli pozyskiwanie i zwalnianie zasobów jest rozłożone na różne części programu. Ale jeśli jest zlokalizowane, jak w twoim przykładzie, możesz łatwo użyć tych bloków zasobów.
BTW: we współczesnym języku C # using
jest w rzeczywistości zbędny, ponieważ możesz samodzielnie implementować bloki zasobów w stylu 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
bloku.