Ruby中的RAII(或者,如何在Ruby中管理资源)
-
03-07-2019 - |
题
我知道你的设计是无法控制对象被销毁时会发生什么。我也知道将一些类方法定义为终结器。
然而,C ++的RAII的ruby习惯用法(资源在构造函数中初始化,在析构函数中关闭)?即使出现错误或异常,人们如何管理对象内部使用的资源?
使用确保有效:
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
但是每次需要调用open方法时,类的用户必须记住要执行整个begin-rescue-ensure chacha 。
例如,我将拥有以下类:
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 ++一样吗?
解决方案
所以用户不要“必须记住做整个开始救援 - 确保chacha ”将 rescue
/ 与
。 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
其他提示
如何将产生
资源发送到块?例如:
File.open("testfile") do |f|
begin
# .. process
rescue
# .. handle error
end
end
或者问题是我还在做这个C ++一样吗?
是的,因为在C ++资源中,对于堆栈中的所有内容都会隐式发生释放。堆栈展开=资源被破坏= destructors被调用,从那里可以释放东西。由于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一样。