Inizia, salva e assicurati in Ruby?

Di recente ho iniziato a programmare in Ruby e sto esaminando la gestione delle eccezioni.

Mi stavo chiedendo se ensure l’equivalente di Ruby di finally in C #? Dovrei avere:

 file = File.open("myFile.txt", "w") begin file << "#{content} \n" rescue #handle the error here ensure file.close unless file.nil? end 

o dovrei farlo?

 #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 

ensure essere chiamato indipendentemente da cosa, anche se non viene sollevata un’eccezione?

Sì, assicurati che il codice venga sempre valutato. Ecco perché si chiama ensure . Quindi, è equivalente a Java e C # finally .

Il stream generale di begin / rescue / else / ensure / end presenta così:

 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 lasciare il rescue , ensure o else . Puoi anche omettere 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 ‘hacky.) E puoi escludere la class di eccezione, nel qual caso verranno catturate tutte le eccezioni ereditate da StandardError . (Si prega di notare che questo non significa che tutte le eccezioni sono intercettate, perché ci sono eccezioni che sono istanze di Exception ma non StandardError . Principalmente eccezioni molto gravi che compromettono l’integrità del programma come SystemStackError , NoMemoryError , SecurityError , NotImplementedError , LoadError , SyntaxError , ScriptError , Interrupt , SignalException o SystemExit .)

Alcuni blocchi formano blocchi di eccezioni implicite. Ad esempio, le definizioni dei metodi sono implicitamente anche blocchi di eccezioni, quindi anziché scrivere

 def foo begin # ... rescue # ... end end 

scrivi solo

 def foo # ... rescue # ... end 

o

 def foo # ... ensure # ... end 

Lo stesso vale per class definizioni delle class e le definizioni dei module .

Tuttavia, nel caso specifico che stai chiedendo, c’è in realtà un idioma molto migliore. In generale, quando lavori con alcune risorse che devi pulire alla fine, lo fai passando un blocco a un metodo che esegue la pulizia per te. È simile a un blocco using in C #, tranne per il fatto che Ruby è abbastanza potente da non dover aspettare che i sommi sacerdoti di Microsoft scendano dalla montagna e cambino 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 è ansible utilizzare anche nel proprio codice, per implementare qualsiasi tipo di pulizia delle risorse ( using in C #) o transazioni o qualsiasi altra cosa si possa pensare.

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.


BTW: nel moderno C #, l’ using è in realtà superfluo, perché puoi implementare blocchi di risorse in stile Ruby:

 class File { static T open(string filename, string mode, Func block) { var handle = new File(filename, mode); try { return block(handle); } finally { handle.Dispose(); } } } // Usage: File.open("myFile.txt", "w", (file) => { file.WriteLine(contents); }); 

FYI, anche se un’eccezione viene sollevata di nuovo nella sezione di rescue , il blocco di ensure verrà eseguito prima che l’esecuzione del codice continui al successivo gestore di eccezioni. Per esempio:

 begin raise "Error!!" rescue puts "test1" raise # Reraise exception ensure puts "Ensure block" end 

Se vuoi assicurarti che un file sia chiuso devi usare il modulo di blocco di File.open :

 File.open("myFile.txt", "w") do |file| begin file << "#{content} \n" rescue #handle the error here end end 

Sì, ensure venga chiamato in ogni circostanza. Per maggiori informazioni vedi ” Eccezioni, catture e gettoni ” del libro Ruby di programmazione e cerca “assicurati”.

Sì, ensure ENSURES venga eseguito ogni volta, quindi non hai bisogno del file.close nel blocco di file.close .

A proposito, un buon modo per testare è fare:

 begin # Raise an error here raise "Error!!" rescue #handle the error here ensure p "=========inside ensure block" end 

È ansible verificare se “========= inside assicurare il blocco” verrà stampato quando c’è un’eccezione. Quindi puoi commentare la dichiarazione che solleva l’errore e vedere se l’istruzione di ensure viene eseguita vedendo se qualcosa viene stampato.

Questo è il motivo per cui abbiamo bisogno di ensure :

 def hoge begin raise rescue raise # raise again ensure puts 'ensure' # will be executed end puts 'end of func' # never be executed end 

Sì, ensure che alla finally garantisca che il blocco verrà eseguito . Questo è molto utile per assicurarsi che le risorse critiche siano protette, ad esempio chiudere un handle di file in caso di errore o rilasciare un mutex.