Pokrenuti, spremiti i osigurati u Ruby?

Nedavno sam počeo programirati u Rubyu i razmišljam o rješavanju iznimki.

Sam bio izvjedljiv ako ensure ekvivalent Ruby finally u C #? Trebam li:

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

ili bih to trebao učiniti?

 #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 

Je li ensure bez obzira na sve, čak i ako se ne dogodi iznimka?

451
03 февр. postavila ThePower 03. veljače. 2010-02-03 14:54 '10 u 14:54 2010-02-03 14:54
@ 7 odgovora

Da, ensure da se kod uvijek vrednuje. Zato je zvao ensure . Dakle, ovo je jednako Java i C # finally .

Opći stream / rescue / else / ensure / end stream izgleda ovako:

 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žete ostaviti rescue , ensure ili else . Također možete ostaviti varijable, u kojem slučaju nećete moći provjeriti izuzetak u kodu za rukovanje iznimkama. (Pa, uvijek možete koristiti globalnu varijablu izuzetka za pristup posljednjoj iznimci koja je podignuta, ali je malo napukla.) I možete ostaviti klasu iznimke, iu ovom slučaju, sve iznimke koje nasljeđuju od StandardError će biti uhvaćene. (Imajte na umu da to ne znači da su sve iznimke uhvaćene, jer postoje iznimke koje su primjerci Exception , ali ne i StandardError . Uglavnom vrlo ozbiljne iznimke koje krše integritet programa, na primjer NoMemoryError , NoMemoryError , SecurityError , NotImplementedError , LoadError , SyntaxError , ScriptError , Interrupt , SignalException ili SystemExit .)

Neki blokovi tvore implicitne blokove izuzetaka. Na primjer, definicije metoda implicitno su i blokovi iznimke, pa umjesto pisanja

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

samo pišete

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

ili

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

Isto vrijedi i za definicije class definicije module .

Međutim, u konkretnom slučaju o kojem se pitate, zapravo postoji mnogo bolji idiom. Općenito, kada radite s nekim resursom koji trebate očistiti na kraju, učinite to prosljeđivanjem bloka metodi koja obavlja sve čišćenje za vas. Izgleda kao using bloka u C #, osim što je Ruby zapravo prilično moćan, a ne morate čekati da veliki svećenici Microsofta sidju s planine i ljubazno promijenite kompajler za vas. U Ruby-u možete ga jednostavno sami implementirati:

 # 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 unless filehandle.nil? end 

A što znate: već je dostupna u glavnoj knjižnici kao File.open . No, to je opći uzorak koji možete koristiti u vlastitom kodu za provedbu bilo kakvog čišćenja resursa (a la using u C #) ili transakcija ili bilo što drugo što biste mogli misliti.

Jedini slučaj u kojem to ne radi je ako se primanje i otpuštanje resursa distribuira različitim dijelovima programa. Ali ako je lokaliziran, kao u vašem primjeru, možete jednostavno koristiti ove blokove resursa.


BTW: u suvremenom C #, using je zapravo suvišna, jer sami možete implementirati Ruby blokove resursa:

 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); }); 
999
03 февр. Odgovor daje Jörg W Mittag 03. veljače. 2010-02-03 16:04 '10 u 16:04 2010-02-03 16:04

FYI, čak i ako je iznimka ponovno definirana u odjeljku za rescue , blok ensure će se izvršiti prije nego što se kod nastavi s narednim rukovateljem iznimke. Na primjer:

border=0
 begin raise "Error!!" rescue puts "test1" raise # Reraise exception ensure puts "Ensure block" end 
29
29 окт. odgovor je dan 29. listopada. 2012-10-29 15:18 '12 u 15:18 2012-10-29 15:18

Ako želite da se datoteka zatvori, morate koristiti blok File.open :

 File.open("myFile.txt", "w") do |file| begin file << "#{content} \n" rescue #handle the error here end end 
11
03 февр. Farrelov odgovor 03. veljače. 2010-02-03 15:08 '10 u 15:08 2010-02-03 15:08

Da, ensure se poziva pod bilo kojim okolnostima. Više informacija potražite u odjeljku "Iznimke, zahvat i bacanje" u knjizi Ruby programiranje i traženje "pružanje".

6
03 февр. Odgovor daje Milan Novota 03. veljače. 2010-02-03 14:57 '10 u 2:57 2010-02-03 14:57

Zbog toga moramo ensure :

 def hoge begin raise rescue raise # raise again ensure puts 'ensure' # will be executed end puts 'end of func' # never be executed end 
3
23 янв. Odgovor je dan kuboon 23 jan. 2014-01-23 10:27 '14 u 10:27 2014-01-23 10:27

Da, ensure na finally osigura da će blok biti izvršen . To je, na primjer, vrlo korisno za osiguravanje zaštite kritičnih resursa. zatvaranje ručice datoteke na pogrešku ili oslobađanje mutexa.

3
03 февр. Odgovor je dao Chris McCauley 3. veljače 2010-02-03 15:09 '10 u 15:09 2010-02-03 15:09

Da, ensure ENSURES pokreće svaki put, tako da ne trebate file.close u file.close bloku.

Usput, dobar način da provjerite:

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

Možete provjeriti ispisuje li se "========== unutar zajamčenog bloka" kada dođe do iznimke. Tada možete komentirati izraz koji uzrokuje pogrešku i vidjeti je li izjava o ensure izvršena ako se nešto ispiše.

3
03 февр. Odgovor je dao Aaron Qian 3. veljače. 2010-02-03 15:10 '10 u 15:10 2010-02-03 15:10