RAII in Ruby (Oder wie Verwalten von Ressourcen in Ruby)
-
03-07-2019 - |
Frage
Ich weiß, dass es durch Design, das Sie nicht kontrollieren können, was passiert, wenn ein Objekt zerstört wird. Ich bin mir auch bewusst eine Klassenmethode als Finalizerthread zu definieren.
Allerdings ist der Rubin Idiom für C ++ 's RAII (Ressourcen werden im Konstruktor initialisiert, in destructor geschlossen)? Wie verwalte Menschen innerhalb Objekte verwendeten Ressourcen, selbst wenn Fehler oder Ausnahmen passieren?
Mit gewährleisten funktioniert:
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
aber die Benutzer der Klasse haben sich daran zu erinnern chacha zu tun die ganze beginnen-Rettungs gewährleisten , jedes Mal die offene Methode muss aufgerufen werden.
So zum Beispiel, werde ich die folgende Klasse:
class SomeResource
def initialize(connection_string)
@resource_handle = ...some mojo here...
end
def do_something()
begin
@resource_handle.do_that()
...
rescue
...
ensure
end
def close
@resource_handle.close
end
end
Die resource_handle wird nicht geschlossen werden, wenn die Ausnahme Ursache durch eine andere Klasse ist und das Skript beendet wird.
Oder ist das Problem von Ich tue dies immer noch zu C ++ - wie
Lösung
Damit Benutzer nicht " müssen bedenken, zu tun die ganze beginnen-Rettungs gewährleisten chacha " kombinieren rescue
/ ensure
mit yield
.
class SomeResource
...
def SomeResource.use(*resource_args)
# create resource
resource = SomeResource.new(*resource_args) # pass args direct to constructor
# export it
yield resource
rescue
# known error processing
...
ensure
# close up when done even if unhandled exception thrown from block
resource.close
end
...
end
Client-Code kann es wie folgt verwenden:
SomeResource.use(connection_string) do | resource |
resource.do_something
... # whatever else
end
# after this point resource has been .close()d
In der Tat ist dies, wie File.open
arbeitet -. Die erste Antwort macht bestenfalls verwirrend (naja, es war zu mein Arbeitskollegen)
File.open("testfile") do |f|
# .. process - may include throwing exceptions
end
# f is guaranteed closed after this point even if exceptions are
# thrown during processing
Andere Tipps
Wie wäre es, eine Ressource zu einem Block yield
ing? Beispiel:
File.open("testfile") do |f|
begin
# .. process
rescue
# .. handle error
end
end
Oder ist das Problem von Ich tue dies immer noch zu C ++ - wie
Ja, es ist da in C ++ Ressourcen-Freigabe auf dem Stapel implizit für alles geschieht. Stapel abgewickelt = resource = Destruktoren zerstört genannt und von dort Dinge gelöst werden. Da Rubin keine Destruktoren hat es keine „tun, wenn alles andere mit getan wird“ Platz da grabage Sammlung von mehreren Zyklen verzögert werden kann, wo Sie sind. Sie haben Finalizers aber sie sind „in der Schwebe“ (nicht alles zur Verfügung, um sie) und sie erhalten aufgerufen, GC genannt.
Wenn Sie also einen Griff auf eine Ressource halten, die besser gelöst werden Sie es explizit freigeben müssen. In der Tat das richtige Idiom diese Art von Situation zu handhaben ist
def with_shmoo
handle = allocate_shmoo
yield(handle)
ensure
handle.close
end
Siehe http://www.rubycentral.com/pickaxe/tut_exceptions.html
In Ruby, würden Sie eine ensure
Anweisung verwenden:
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
Dies wird für Benutzer von Python, Java oder C # vertraut sein, dass es wie try / catch arbeitet / finally.