RAII em Ruby (Ou, Como Gerenciar Recursos em Ruby)
-
03-07-2019 - |
Pergunta
Eu sei que é por design que você não pode controlar o que acontece quando um objeto é destruído. Também estou ciente de definir algum método de classe como um finalizador.
No entanto é o idioma rubi para C ++ 's RAII (Recursos são inicializados no construtor, fechado em destructor)? Como as pessoas gerenciar recursos utilizados dentro de objetos, mesmo quando erros ou exceções acontecer?
Usando garantir funciona:
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
mas os usuários da classe tem que se lembrar de fazer o todo Begin-socorro-garantem chacha sempre que o abertos necessidades método a ser chamado.
Assim, por exemplo, eu vou ter a seguinte classe:
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
O resource_handle não será fechado se a exceção é causa por alguma outra classe e as saídas de script.
Ou o problema é mais do que eu ainda estou fazendo isso também C ++ -? Como
Solução
Assim que os usuários não " tem que se lembrar de fazer o todo começam-socorro-garantir chacha " combinar rescue
/ ensure
com 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
O código do cliente pode usá-lo da seguinte forma:
SomeResource.use(connection_string) do | resource |
resource.do_something
... # whatever else
end
# after this point resource has been .close()d
Na verdade, esta é a forma como File.open
opera -. Fazer a primeira resposta confuso no melhor (bem foi a minha colegas de trabalho)
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
Outras dicas
Como cerca yield
ing um recurso a um bloco? Exemplo:
File.open("testfile") do |f|
begin
# .. process
rescue
# .. handle error
end
end
Ou o problema é mais do que eu ainda estou fazendo isso também C ++ -? Como
Sim, é uma vez que em C ++ desalocação de recursos acontece de forma implícita para tudo na pilha. Pilha desenrolado = recurso destruída = destruidores chamados e de lá coisas podem ser liberados. Desde o Ruby não tem destruidores não há "fazer isso quando tudo o resto é feito com" vigor desde coleção grabage pode ser adiada vários ciclos de onde você está. Você tem finalizadores, mas eles são chamados de "no limbo" (nem tudo está disponível para eles) e eles são chamados de GC.
Portanto, se você estiver segurando um identificador para algum recurso que melhor ser lançado você precisa liberá-lo explicitamente. Na verdade, a expressão correta de lidar com este tipo de situação é
def with_shmoo
handle = allocate_shmoo
yield(handle)
ensure
handle.close
end
http://www.rubycentral.com/pickaxe/tut_exceptions.html
Em Ruby, você usaria uma declaração ensure
:
f = File.open("testfile")
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
Esta será familiar aos usuários de Python, Java ou C #, em que ele funciona como try / catch / finally.