Question Commencer, sauver et assurer en Ruby?


J'ai récemment commencé à programmer en Ruby, et je regarde la gestion des exceptions.

je me demandais si ensure était l'équivalent de Ruby de finally 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 ensure se faire appeler n'importe quoi, même si une exception n'est pas soulevée?


466
2018-02-03 11:54


origine


Réponses:


Oui, ensure s'assure que le code est toujours évalué. C'est pourquoi ça s'appelle ensure. Donc, il est équivalent à Java et C # 's 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 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é soulevée, mais c'est un peu hacky.) Et vous pouvez omettre la classe d'exception, auquel cas toutes les exceptions qui héritent de StandardError sera attrapé. (Veuillez noter que cela ne signifie pas que tout les exceptions sont prises, car il existe des exceptions qui sont des instances de Exception mais non StandardError. Principalement des exceptions très sévères qui compromettent l'intégrité du programme tels que SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, 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

de même pour class définitions et module définitions

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. C'est semblable à un using bloquer en C #, sauf que Ruby est vraiment 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 qu'est-ce que tu sais: c'est déjà disponible dans la bibliothèque principale en tant que 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 des ressources (à la using en C #) ou des transactions ou tout ce que vous pourriez penser.

Le seul cas où cela ne fonctionne pas, si l'acquisition et la publication de la ressource sont répartis 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 superflu, 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);
});

1031
2018-02-03 13:04



FYI, même si une exception est re-soulevée dans le rescue section, le ensure Le bloc sera exécuté avant que l'exécution du code ne se poursuive au prochain gestionnaire d'exceptions. Par exemple:

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

30
2017-10-29 12:18



Si vous voulez vous assurer qu'un fichier est fermé, vous devez utiliser la forme de bloc File.open:

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

11
2018-02-03 12:08



Oui, ensure est appelé en toutes circonstances. Pour plus d'informations, voir "Exceptions, Catch et Throw"du livre Programmation Ruby et chercher" assurer ".


6
2018-02-03 11:57



Oui, ensure ASSURE qu'il est exécuté à chaque fois, de sorte que vous n'avez pas besoin de file.close dans le begin bloc.

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 si "========= inside assurer bloc" sera imprimé en cas d'exception. Ensuite, vous pouvez commenter la déclaration qui soulève l'erreur et voir si le ensure La déclaration est exécutée en voyant si quelque chose est imprimé.


3
2018-02-03 12:10



C'est pourquoi nous avons besoin 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
2018-01-23 07:27



Oui, ensure comme finally  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 fermer un descripteur de fichier en cas d'erreur ou libérer un mutex.


3
2018-02-03 12:09