Oui, ensure
garantit que le code est toujours évalué. Voilà pourquoi ça s'appelle ensure
. Donc, c'est équivalent à Java et C # finally
.
Le flux général de begin
/ rescue
/ else
/ ensure
/ end
ressemble à ceci:
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
Vous pouvez laisser de côté rescue
, ensure
ou else
. Vous pouvez également laisser de côté les variables, auquel cas vous ne pourrez pas inspecter l'exception dans votre code de gestion des exceptions. (Eh bien, vous pouvez toujours utiliser la variable d'exception globale pour accéder à la dernière exception qui a été déclenchée, mais c'est un peu hacky.) Et vous pouvez laisser de côté la classe d'exception, auquel cas toutes les exceptions qui héritent de StandardError
seront interceptées. (S'il vous plaît noter que cela ne signifie pas que toutes les exceptions sont prises, car il y a des exceptions qui sont des instances de Exception
non StandardError
. La plupart du temps des exceptions très graves qui compromettent l'intégrité du programme tel que SystemStackError
, NoMemoryError
,SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
,SignalException
ou SystemExit
.)
Certains blocs forment des blocs d'exception implicites. Par exemple, les définitions de méthode sont implicitement aussi des blocs d'exception, donc au lieu d'écrire
def foo
begin
# ...
rescue
# ...
end
end
tu écris juste
def foo
# ...
rescue
# ...
end
ou
def foo
# ...
ensure
# ...
end
Il en va de même pour les class
définitions et module
définitions.
Cependant, dans le cas spécifique que vous posez, il existe en fait un bien meilleur idiome. En général, lorsque vous travaillez avec une ressource que vous devez nettoyer à la fin, vous le faites en passant un bloc à une méthode qui fait tout le nettoyage pour vous. C'est similaire à un using
bloc en C #, sauf que Ruby est en fait assez puissant pour que vous n'ayez pas à attendre que les grands prêtres de Microsoft descendent de la montagne et changent gracieusement leur compilateur pour vous. Dans Ruby, vous pouvez simplement l'implémenter vous-même:
# 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
Et que savez-vous: c'est déjà disponible dans la bibliothèque de base en tant que File.open
. Mais c'est un modèle général que vous pouvez également utiliser dans votre propre code, pour implémenter tout type de nettoyage de ressources (à la using
en C #) ou de transactions ou tout ce que vous pourriez penser.
Le seul cas où cela ne fonctionne pas, si l'acquisition et la libération de la ressource sont réparties sur différentes parties du programme. Mais s'il est localisé, comme dans votre exemple, vous pouvez facilement utiliser ces blocs de ressources.
BTW: en C # moderne, using
est en fait superflu, car vous pouvez implémenter vous-même des blocs de ressources de style 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
bloc.