Sì, ensure
garantisce che il codice sia sempre valutato. Ecco perché si chiama ensure
. Quindi, è equivalente a Java e C # finally
.
Il flusso generale di begin
/ rescue
/ else
/ ensure
/ end
è simile al seguente:
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
Puoi lasciarlo fuori rescue
, ensure
oppure else
. Puoi anche tralasciare le variabili, nel qual caso non sarai in grado di ispezionare l'eccezione nel tuo codice di gestione delle eccezioni. (Bene, puoi sempre usare la variabile di eccezione globale per accedere all'ultima eccezione che è stata sollevata, ma è un po 'confusa.) E puoi tralasciare la classe di eccezione, nel qual caso tutte le eccezioni che ereditano StandardError
verranno colte. (Si noti che questo non significa che tutte le eccezioni vengono catturati, perché ci sono delle eccezioni, che sono istanze di Exception
, ma non StandardError
. Per lo più molto gravi eccezioni che compromettono l'integrità del programma come SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
,SignalException
o SystemExit
.)
Alcuni blocchi formano blocchi di eccezioni impliciti. Ad esempio, le definizioni dei metodi sono implicitamente anche blocchi di eccezioni, quindi invece di scrivere
def foo
begin
# ...
rescue
# ...
end
end
scrivi solo
def foo
# ...
rescue
# ...
end
o
def foo
# ...
ensure
# ...
end
Lo stesso vale per le class
definizioni e le module
definizioni.
Tuttavia, nel caso specifico di cui ti stai chiedendo, in realtà esiste un linguaggio molto migliore. In generale, quando lavori con alcune risorse che devi ripulire alla fine, lo fai passando un blocco a un metodo che esegue tutta la pulizia per te. È simile a un using
blocco in C #, tranne per il fatto che Ruby è in realtà abbastanza potente da non dover aspettare che i sommi sacerdoti di Microsoft scendano dalla montagna e cambino gentilmente il loro compilatore per te. In Ruby, puoi semplicemente implementarlo da solo:
# 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
E cosa sai: questo è già disponibile nella libreria principale come File.open
. Ma è un modello generale che puoi usare anche nel tuo codice, per implementare qualsiasi tipo di pulizia delle risorse (à la using
in C #) o transazioni o qualsiasi altra cosa ti venga in mente.
L'unico caso in cui questo non funziona, se l'acquisizione e il rilascio della risorsa sono distribuiti su diverse parti del programma. Ma se è localizzato, come nel tuo esempio, puoi facilmente usare questi blocchi di risorse.
A proposito: nel moderno C #, in using
realtà è superfluo, perché puoi implementare tu stesso blocchi di risorse in stile 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
blocco.