it-swarm.com.de

Beginnen, retten und in Ruby versichern?

Ich habe vor kurzem begonnen, in Ruby zu programmieren, und ich beschäftige mich mit der Ausnahmebehandlung.

Ich habe mich gefragt, ob ensure das Ruby Äquivalent von finally in C # war. Sollte ich haben:

file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end

oder soll ich das machen

#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

Wird ensure auf jeden Fall aufgerufen, auch wenn keine Ausnahme ausgelöst wird?

516
Lloyd Powell

Ja, ensure stellt sicher, dass der Code immer ausgewertet wird. Deshalb heißt es ensure. Es ist also äquivalent zu Java und C # finally.

Der allgemeine Ablauf von begin/rescue/else/ensure/end sieht folgendermaßen aus:

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

Sie können rescue, ensure oder else weglassen. Sie können auch die Variablen weglassen. In diesem Fall können Sie die Ausnahme in Ihrem Ausnahmebehandlungscode nicht überprüfen. (Nun, Sie können immer die globale Ausnahmevariable verwenden, um auf die letzte Ausnahme zuzugreifen, die ausgelöst wurde, aber das ist ein bisschen kitschig.) Und Sie können die Ausnahmeklasse weglassen. In diesem Fall werden alle Ausnahmen abgefangen, die von StandardError erben. (Bitte beachten Sie, dass dies nicht bedeutet, dass alle Ausnahmen abgefangen werden, da es Ausnahmen gibt, bei denen es sich um Exception, aber nicht um StandardError handelt. Meist sehr schwerwiegende Ausnahmen, die die Integrität des Programms beeinträchtigen, wie SystemStackError, NoMemoryError__ , SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt, SignalException oder SystemExit.)

Einige Blöcke bilden implizite Ausnahmeblöcke. Beispielsweise sind Methodendefinitionen implizit auch Ausnahmeblöcke, also anstatt zu schreiben

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

du schreibst einfach

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

oder

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

Gleiches gilt für class-Definitionen und module-Definitionen.

In dem speziellen Fall, nach dem Sie fragen, gibt es jedoch eine viel bessere Sprache. Wenn Sie mit einer Ressource arbeiten, die Sie am Ende bereinigen müssen, übergeben Sie im Allgemeinen einen Block an eine Methode, die die gesamte Bereinigung für Sie ausführt. Es ähnelt einem using -Block in C #, mit der Ausnahme, dass Ruby tatsächlich mächtig genug ist, dass Sie nicht darauf warten müssen, dass die Hohepriester von Microsoft vom Berg herunterkommen und ihren Compiler gnädig für Sie ändern. In Ruby können Sie es einfach selbst implementieren:

# 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

Und was wissen Sie: Dies ist bereits in der Kernbibliothek als File.open verfügbar. Es ist jedoch ein allgemeines Muster, das Sie auch in Ihrem eigenen Code verwenden können, um jede Art von Ressourcenbereinigung (à la using in C #) oder Transaktionen oder was auch immer Sie sich sonst vorstellen.

Der einzige Fall, in dem dies nicht funktioniert, wenn der Erwerb und die Freigabe der Ressource auf verschiedene Teile des Programms verteilt sind. Wenn es jedoch wie in Ihrem Beispiel lokalisiert ist, können Sie diese Ressourcenblöcke problemlos verwenden.


Übrigens: In modernem C # ist using eigentlich überflüssig, da Sie Ressourcenblöcke im Ruby-Stil selbst implementieren können:

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);
});
1133
Jörg W Mittag

Selbst wenn eine Ausnahme im Abschnitt rescue erneut ausgelöst wird, wird der Block ensure ausgeführt, bevor die Codeausführung mit der nächsten Ausnahmebehandlung fortgesetzt wird. Zum Beispiel:

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

Wenn Sie sicherstellen möchten, dass eine Datei geschlossen ist, sollten Sie die Blockform File.open verwenden:

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

Ja, ensure wird unter keinen Umständen aufgerufen. Weitere Informationen finden Sie unter " Exceptions, Catch and Throw " im Buch Programming Ruby und suchen Sie nach "verify".

6
Milan Novota

Ja, ensure STELLT sicher, dass es jedes Mal ausgeführt wird, sodass Sie den file.close im begin -Block nicht benötigen.

Übrigens ist ein guter Weg, um zu testen, zu tun:

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

Sie können testen, ob "========= innerhalb des Sicherstellungsblocks" gedruckt wird, wenn eine Ausnahme vorliegt. Anschließend können Sie die Anweisung, die den Fehler auslöst, auskommentieren und prüfen, ob die Anweisung ensure ausgeführt wird, indem Sie prüfen, ob etwas ausgedruckt wird.

4
Aaron Qian

Ja, ensure wie finally garantiert, dass der Block ausgeführt wird. Dies ist sehr nützlich, um sicherzustellen, dass kritische Ressourcen geschützt sind, z. Schließen eines Datei-Handles bei einem Fehler oder Freigeben eines Mutex.

3
Chris McCauley

Deshalb brauchen wir 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
kuboon