代码之家  ›  专栏  ›  技术社区  ›  moogs

Ruby中的raii(或者,如何管理Ruby中的资源)

  •  9
  • moogs  · 技术社区  · 16 年前

    我知道,当一个物体被摧毁时,你无法控制它的设计。我还知道将一些类方法定义为终结器。

    然而,C++的RAII的Ruby成语(资源在构造函数中初始化,在析构函数中被关闭)?即使发生错误或异常,人们如何管理对象内部使用的资源?

    使用 确保 作品:

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

    但是这个类的用户 必须记住要做的全部,开始营救,确保查查 每次需要调用open方法时。

    例如,我将有以下类:

    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
    

    如果异常是由其他类引起的,并且脚本退出,则不会关闭资源句柄。

    或者问题是,我还是像C++一样这么做?

    4 回复  |  直到 13 年前
        1
  •  15
  •   Greg    15 年前

    这样用户就不会 必须记住要做的全部,开始营救,确保查查 联合收割机 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
    
        2
  •  8
  •   bk1e    16 年前

    怎么样 yield 将资源添加到块?例子:

    File.open("testfile") do |f|
      begin
        # .. process
      rescue
        # .. handle error
      end
    end
    
        3
  •  3
  •   Julik    13 年前

    或者问题是,我还是像C++一样这么做?

    是的,因为在C++资源中,堆栈上的所有内容都会发生隐式分配。堆栈UNVOUND=resource destroyed=destructors调用后,可以从中释放内容。因为Ruby没有析构函数,所以不存在“当其他事情都完成了的时候就去做”的地方,因为Grabage收集可以从您所在的位置延迟几个周期。您确实有终结器,但它们被称为“in-limbo”(并非所有内容都对它们可用),并且它们在GC上被调用。

    因此,如果您持有某个更好地被释放的资源的句柄,那么您需要显式地释放它。事实上,处理这种情况的正确习惯用法是

    def with_shmoo
      handle = allocate_shmoo
      yield(handle)
    ensure
      handle.close
    end
    
        4
  •  -1
  •   John Millikin    16 年前

    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语言的用户来说是熟悉的,因为它的工作方式类似于尝试/捕获/最终。