Question

Je sais que c'est par conception que vous ne pouvez pas contrôler ce qui se produit lorsqu'un objet est détruit. Je suis également conscient de la définition d’une méthode de classe comme finaliseur.

Cependant, l'idiome ruby ??pour RAII de C ++ (les ressources sont initialisées dans le constructeur, fermées dans le destructeur)? Comment les personnes gèrent-elles les ressources utilisées dans les objets même lorsque des erreurs ou des exceptions se produisent?

L'utilisation de sure fonctionne:

f = File.open("testfile")
begin
  # .. process
rescue
  # .. handle error
ensure
  f.close unless f.nil?
end

mais les utilisateurs de la classe ne doivent pas oublier de faire tout le processus begin-rescue-guarantee chacha chaque fois que la méthode open doit être appelée.

Ainsi, par exemple, j'aurai la classe suivante:

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

Le resource_handle ne sera pas fermé si l'exception est causée par une autre classe et que le script se ferme.

Ou le problème est-il davantage lié au fait que je le fais encore - comme C ++?

Était-ce utile?

La solution

Pour que les utilisateurs ne soient pas " obligés de se rappeler de faire tout le processus begin-rescue-assure chacha ". combinez rescue / assure avec rendement .

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

Le code client peut l’utiliser comme suit:

SomeResource.use(connection_string) do | resource |
  resource.do_something
  ... # whatever else
end
# after this point resource has been .close()d

En fait, voici comment File.open fonctionne - rendant la première réponse confuse au mieux (bien c’était à mes collègues de travail).

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

Autres conseils

Que diriez-vous de céder une ressource à un bloc? Exemple:

File.open("testfile") do |f|
  begin
    # .. process
  rescue
    # .. handle error
  end
end
  

Ou le problème est-il davantage lié au fait que je le fais encore - comme C ++?

Oui, cela se produit depuis que la désallocation de ressources C ++ est implicite pour tout ce qui se trouve sur la pile. Pile déroulée = ressource détruite = destructeurs appelés et à partir de là des choses peuvent être libérées. Comme Ruby n’a pas de destructeurs, il n’ya pas de "faire cela quand tout le reste est fait avec". place puisque la collecte des déchets peut être retardée de plusieurs cycles de l'endroit où vous vous trouvez. Vous avez des finaliseurs, mais ils sont appelés "dans les limbes". (tout ne leur est pas accessible) et ils sont appelés sur GC.

Par conséquent, si vous détenez un descripteur sur une ressource qu'il vaut mieux libérer, vous devez le divulguer explicitement. En effet, le bon idiome pour gérer ce genre de situation est

def with_shmoo
  handle = allocate_shmoo
  yield(handle)
ensure
  handle.close
end

Voir http://www.rubycentral.com/pickaxe/tut_exceptions.html

En Ruby, vous utiliseriez une instruction assure :

f = File.open("testfile")
begin
  # .. process
rescue
  # .. handle error
ensure
  f.close unless f.nil?
end

Ceci sera familier aux utilisateurs de Python, Java ou C # en ce sens que cela fonctionne comme try / catch / finally.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top