Вопрос

Я знаю, что так задумано, что вы не можете контролировать, что происходит, когда объект уничтожается.Мне также известно об определении некоторого метода класса в качестве финализатора.

Однако является ли рубиновая идиома для RAII С++ (ресурсы инициализируются в конструкторе, закрываются в деструкторе)?Как люди управляют ресурсами, используемыми внутри объектов, даже если случаются ошибки или исключения?

С использованием гарантировать работает:

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

но пользователи класса нужно не забыть сделать всю чачу «начать-спасти-обеспечить» каждый раз, когда необходимо вызвать открытый метод.

Например, у меня будет следующий класс:

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

Resource_handle не будет закрыт, если исключение вызвано каким-либо другим классом и скрипт завершится.

Или проблема в том, что я все еще делаю это слишком похоже на C++?

Это было полезно?

Решение

Чтобы пользователи не "нужно не забыть сделать всю чачу «начать-спасти-обеспечить»"объединить rescue/ensure с 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

Клиентский код может использовать его следующим образом:

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

На самом деле вот так File.open работает - первый ответ в лучшем случае сбивает с толку (ну, это было мой коллеги по работе).

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

Другие советы

Как насчет yieldвключение ресурса в блок?Пример:

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

Или проблема в том, что я все еще делаю это слишком похоже на C++?

Да, это так, поскольку в C++ освобождение ресурсов происходит неявно для всего, что находится в стеке.Стек размотан = ресурс уничтожен = вызваны деструкторы, и оттуда все можно освободить.Поскольку в Ruby нет деструкторов, нет места «сделать это, когда все остальное будет сделано», поскольку сбор мусора может быть отложен на несколько циклов по сравнению с тем местом, где вы находитесь.У вас есть финализаторы, но они называются «в подвешенном состоянии» (им доступно не все) и они вызываются на GC.

Поэтому, если вы удерживаете дескриптор какого-то ресурса, который лучше освободить, вам необходимо освободить его явно.Действительно, правильная идиома для решения такой ситуации —

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

Видеть http://www.rubycentral.com/pickaxe/tut_Exceptions.html

В Ruby вы бы использовали ensure заявление:

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

Это будет знакомо пользователям Python, Java или C#, поскольку оно работает как try/catch/finally.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top