Rozpocząć, uratować i zapewnić w Ruby?
Niedawno zacząłem programować w Rubim i patrzę na obsługę wyjątków.
Zastanawiałem się, czy ensure
jest odpowiednikiem Rubiego finally
W C#? Powinienem:
file = File.open("myFile.txt", "w")
begin
file << "#{content} \n"
rescue
#handle the error here
ensure
file.close unless file.nil?
end
Czy powinienem to zrobić?
#store the file
file = File.open("myFile.txt", "w")
begin
file << "#{content} \n"
file.close
rescue
#handle the error here
ensure
file.close unless file.nil?
end
Czy ensure
jest wywoływany bez względu na wszystko, nawet jeśli wyjątek nie jest podniesiony?
7 answers
Tak, ensure
zapewnia, że kod jest zawsze oceniany. Dlatego nazywa się ensure
. Jest więc odpowiednikiem Javy i C#finally
.
Ogólny przepływ begin
/rescue
/else
/ensure
/end
wygląda tak:
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żesz pominąć rescue
, ensure
lub else
. Możesz również pominąć zmienne, w takim przypadku nie będziesz mógł sprawdzić wyjątku w kodzie obsługi wyjątków. (Cóż, zawsze możesz użyć zmiennej Global exception, aby uzyskać dostęp do ostatniego wyjątku to było podniesione, ale to trochę za trudne.) I możesz pominąć klasę exception, w którym to przypadku wszystkie wyjątki dziedziczone z StandardError
zostaną przechwycone. (Proszę zauważyć, że nie oznacza to, że wszystkie wyjątki są przechwytywane, ponieważ istnieją wyjątki, które są instancjami Exception
, ale nie StandardError
. Głównie bardzo poważne wyjątki, które zagrażają integralności programu, takie jak SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
, SignalException
lub SystemExit
.)
Niektóre bloki tworzą ukryte bloki WYJĄTKÓW. Na przykład definicje metod są domyślnie również blokami WYJĄTKÓW, więc zamiast pisać
def foo
begin
# ...
rescue
# ...
end
end
Piszesz tylko
def foo
# ...
rescue
# ...
end
Lub
def foo
# ...
ensure
# ...
end
To samo dotyczy definicji class
i definicji module
.
Jednak w konkretnym przypadku, o który pytasz, istnieje znacznie lepszy idiom. Ogólnie rzecz biorąc, gdy pracujesz z jakimś zasobem, który musisz wyczyścić na końcu, robisz to przekazując blok do metody, która robi wszystko za Ciebie. Jest podobny do bloku using
W C#, z tym że Ruby jest na tyle potężny, że nie musisz czekać, aż arcykapłani Microsoftu zejdą z góry i łaskawie zmienią za Ciebie kompilator. W Ruby możesz po prostu zaimplementować go samodzielnie:
# 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
I co wiecie: to jest już dostępne w Bibliotece Głównej jako File.open
. Ale jest to ogólny wzór, który można wykorzystać w swoim własny kod, jak również, do implementacji wszelkiego rodzaju czyszczenia zasobów (à la using
W C#) lub transakcji lub cokolwiek innego można myśleć.
Jedyny przypadek, w którym to nie działa, jeśli pozyskiwanie i zwalnianie zasobów jest dystrybuowane w różnych częściach programu. Ale jeśli jest zlokalizowany, jak w twoim przykładzie, możesz łatwo użyć tych bloków zasobów.
BTW: w nowoczesnym C#, using
jest rzeczywiście zbędny, ponieważ można zaimplementować bloki zasobów w stylu Ruby siebie:
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);
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-03 10:57:40
Dla twojej wiadomości, nawet jeśli wyjątek zostanie ponownie podniesiony w sekcji rescue
, Blok ensure
zostanie wykonany przed kontynuacją wykonywania kodu do następnego programu obsługi wyjątków. Na przykład:
begin
raise "Error!!"
rescue
puts "test1"
raise # Reraise exception
ensure
puts "Ensure block"
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 23:24:44
Jeśli chcesz mieć pewność, że plik jest zamknięty, powinieneś użyć formy blokowej File.open
:
File.open("myFile.txt", "w") do |file|
begin
file << "#{content} \n"
rescue
#handle the error here
end
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 23:25:13
TAK, ensure
jest wywoływany w każdych okolicznościach. Aby uzyskać więcej informacji zobacz "Exceptions, Catch, and Throw " z podręcznika programowania Ruby i wyszukaj "ensure".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 23:27:11
Tak, ensure
zapewnia, że jest uruchamiany za każdym razem, więc nie potrzebujesz file.close
w bloku begin
.
Przy okazji, dobrym sposobem na test jest zrobienie:
begin
# Raise an error here
raise "Error!!"
rescue
#handle the error here
ensure
p "=========inside ensure block"
end
Możesz sprawdzić, czy "=========inside ensure block " zostanie wydrukowany, gdy wystąpi wyjątek.
Następnie możesz skomentować instrukcję, która powoduje błąd i sprawdzić, czy Instrukcja ensure
jest wykonywana, sprawdzając, czy coś zostanie wydrukowane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 23:29:16
Dlatego potrzebujemy ensure
:
def hoge
begin
raise
rescue
raise # raise again
ensure
puts 'ensure' # will be executed
end
puts 'end of func' # never be executed
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 23:29:32
Tak, ensure
jak finally
gwarantuje wykonanie bloku. Jest to bardzo przydatne, aby upewnić się, że krytyczne zasoby są chronione, np. zamknięcie uchwytu pliku w przypadku błędu lub zwolnienie mutex.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-02 14:30:51