Commencer, sauver et assurer en Ruby?

J’ai récemment commencé à programmer en Ruby, et j’examine la gestion des exceptions.

Je me demandais si l’ ensure était l’équivalent de Ruby en C #? Devrais-je avoir:

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

ou devrais-je le faire?

 #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 

Est-ce que l’ ensure est appelée, peu importe quoi, même si une exception n’est pas soulevée?

Oui, ensure que le code est toujours évalué. C’est pourquoi on l’appelle ensure . Donc, cela équivaut enfin à Java et à C #.

Le stream 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é le rescue , ensure ou else . Vous pouvez également omettre 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 piraté.) Et vous pouvez exclure la classe d’exception, auquel cas (Notez que cela ne signifie pas que toutes les exceptions sont interceptées, car il existe des exceptions, à savoir des Exception mais pas des NoMemoryError StandardError . Surtout des exceptions très graves qui compromettent l’intégrité du programme telles que SystemStackError , 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 

La même chose s’applique aux définitions de class et aux définitions de module .

Cependant, dans le cas spécifique que vous posez, il existe en réalité un langage bien meilleur. 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 effectue tout le nettoyage pour vous. Il est similaire à un bloc using 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: ceci est déjà disponible dans la bibliothèque principale sous le nom 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 place en C #) ou de transactions ou de tout ce à quoi vous pourriez penser.

Le seul cas où cela ne fonctionne pas, si l’acquisition et la publication de la ressource sont dissortingbués 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: dans C # moderne, l’ using est en fait superflue, car vous pouvez implémenter vous-même les blocs de ressources de style Ruby:

 class File { static T open(ssortingng filename, ssortingng 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, même si une exception est relancée dans la section de rescue , le bloc d’ ensure sera exécuté avant que l’exécution du code continue au gestionnaire d’exceptions suivant. Par exemple:

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

Si vous voulez vous assurer qu’un fichier est fermé, vous devez utiliser le formulaire de File.open de File.open :

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

Oui, ensureensure être appelé en toutes circonstances. Pour plus d’informations, reportez-vous à ” Exceptions, Catch et Throw ” du livre Programming Ruby et recherchez “assure”.

Oui, ensure vous ensure ENSURES est exécuté à chaque fois, vous n’avez donc pas besoin du file.close dans le bloc de begin .

En passant, une bonne façon de tester est de faire:

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

Vous pouvez tester pour voir si “========= dans l’intérieur du bloc” sera imprimé quand il y a une exception. Ensuite, vous pouvez commenter l’instruction qui déclenche l’erreur et voir si l’instruction ensure est exécutée en vérifiant si quelque chose est imprimé.

C’est pourquoi nous devons nous ensure :

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

Oui, ensure vous ensure like garantit que le bloc sera exécuté . Ceci est très utile pour vous assurer que les ressources critiques sont protégées, par exemple en fermant un handle de fichier en cas d’erreur ou en libérant un mutex.