Oui, ensure
garantit que le code est toujours évalué. C'est pourquoi on l'appelle ensure
. Il est donc équivalent à celui de Java et de C#. finally
.
Le flux général de begin
/ rescue
/ else
/ ensure
/ end
ressemble à ça :
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
o else
. Vous pouvez également omettre les variables, auquel cas vous ne serez pas en mesure d'inspecter l'exception dans votre code de gestion des exceptions. (Vous pouvez toujours utiliser la variable d'exception globale pour accéder à la dernière exception levée, mais c'est un peu compliqué). Vous pouvez également omettre la classe d'exception, auquel cas toutes les exceptions qui héritent de la classe StandardError
sera attrapé. (Veuillez noter que cela ne signifie pas que tous les exceptions sont capturées, car il existe des exceptions qui sont des instances de Exception
pero no StandardError
. La plupart des exceptions très graves qui compromettent l'intégrité du programme, telles que SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
, SignalException
o SystemExit
.)
Certains blocs forment des blocs d'exception implicites. Par exemple, les définitions de méthodes sont implicitement aussi des blocs d'exception, donc au lieu d'écrire
def foo
begin
# ...
rescue
# ...
end
end
vous écrivez juste
def foo
# ...
rescue
# ...
end
ou
def foo
# ...
ensure
# ...
end
Il en va de même pour class
définitions et module
définitions.
Toutefois, dans le cas précis que vous demandez, il existe en fait une expression bien meilleure. 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
en C#, sauf que Ruby est en fait suffisamment 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. En Ruby, vous pouvez tout 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 qu'est-ce que vous savez : c'est déjà disponible dans la bibliothèque centrale en tant que File.open
. Mais il s'agit d'un modèle général que vous pouvez utiliser dans votre propre code également, pour mettre en œuvre tout type de nettoyage de ressources (à la using
en C#) ou des transactions ou tout autre élément auquel vous pourriez penser.
Le seul cas où cela ne fonctionne pas est celui où l'acquisition et la libération de la ressource sont réparties sur différentes parties du programme. Mais si elle est localisée, comme dans votre exemple, alors vous pouvez facilement utiliser ces blocs de ressources.
BTW : en C# moderne, using
est en fait superflue, 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);
});
2 votes
Ni l'un ni l'autre n'est bon. En règle générale, lorsqu'il s'agit de ressources externes, il faut siempre vous voulez que l'ouverture de la ressource soit à l'intérieur du
begin
bloc.