在 Ruby 中开始、救援和确保?

问题描述 投票:0回答:7

我想知道 Ruby 中的

ensure
是否相当于 C# 中的
finally
?我应该有:

file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
rescue
  # handle the error here
ensure
  file.close unless file.nil?
end

或者我应该这样做吗?

#store the file
file = File.open("myFile.txt", "w")

begin
  file << "#{content} \n"
  file.close
rescue
  # handle the error here
ensure
  file.close unless file.nil?
end

即使没有引发异常,

ensure
也会被调用吗?

ruby exception error-handling
7个回答
1339
投票

是的,

ensure
确保代码始终被评估。这就是为什么它被称为
ensure
。所以,它相当于Java和C#的
finally

begin
/
rescue
/
else
/
ensure
/
end
的一般流程如下:

begin
  # something which might raise an exception
rescue SomeExceptionClass => some_variable
  # code that deals with some exception
rescue SomeOtherException => some_other_variable
  # code that deals with some other exception
else
  # code that runs only if *no* exception was raised
ensure
  # ensure that this code always runs, no matter what
  # does not change the final value of the block
end

您可以省略

rescue
ensure
else
。您还可以省略变量,在这种情况下您将无法在异常处理代码中检查异常。 (嗯,您始终可以使用全局异常变量来访问最后引发的异常,但这有点hacky。)并且您可以省略异常类,在这种情况下,从
StandardError
继承的所有异常都将被捕捉。 (请注意,这并不意味着捕获了所有异常,因为有些异常是
Exception
的实例,但不是
StandardError
。大多数是非常严重的异常,会损害程序的完整性,例如
SystemStackError
NoMemoryError
SecurityError
NotImplementedError
LoadError
SyntaxError
ScriptError
Interrupt
SignalException
SystemExit
。)

一些块形成隐式异常块。例如,方法定义也隐式地是异常块,所以不用写

def foo
  begin
    # ...
  rescue
    # ...
  end
end

你只写

def foo
  # ...
rescue
  # ...
end

def foo
  # ...
ensure
  # ...
end

这同样适用于

class
定义和
module
定义。

但是,在您所询问的具体情况下,实际上有一个更好的习语。一般来说,当您使用最终需要清理的某些资源时,您可以通过将一个块传递给一个为您完成所有清理工作的方法来完成此操作。它类似于 C# 中的

using
块,只不过 Ruby 实际上已经足够强大了,你不必等待微软的大祭司从山上下来,慷慨地为你改变他们的编译器。在 Ruby 中,你可以自己实现它:

# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
  file.puts content
end

# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
  yield filehandle = new(filename, mode, perm, opt)
ensure
  filehandle&.close
end

您知道什么:这已经在核心库中可用,作为 File.open

。但这是一种通用模式,您也可以在自己的代码中使用,用于实现任何类型的资源清理(类似于 C# 中的 
using
)或事务或您可能想到的任何其他内容。

如果获取和释放资源分布在程序的不同部分,则此方法不起作用。但如果它是本地化的,如您的示例所示,那么您可以轻松使用这些资源块。


顺便说一句:在现代 C# 中,

using

 实际上是多余的,因为你可以自己实现 Ruby 风格的资源块:

class File { static T open<T>(string filename, string mode, Func<File, T> block) { var handle = new File(filename, mode); try { return block(handle); } finally { handle.Dispose(); } } } // Usage: File.open("myFile.txt", "w", (file) => { file.WriteLine(contents); });
    

50
投票
rescue

部分重新引发异常,

ensure
块也会在代码执行继续到下一个异常处理程序之前执行。例如:

begin raise "Error!!" rescue puts "test1" raise # Reraise exception ensure puts "Ensure block" end



15
投票
File.open

的块形式:


File.open("myFile.txt", "w") do |file| begin file << "#{content} \n" rescue #handle the error here end end



9
投票
ensure

的原因:


def hoge begin raise rescue raise # raise again ensure puts 'ensure' # will be executed end puts 'end of func' # never be executed end



7
投票
ensure

。有关更多信息,请参阅《Ruby 编程》一书的“

异常、捕获和抛出
”并搜索“ensure”。


5
投票
ensure

确保它每次都运行,因此您不需要

file.close
块中的
begin

顺便说一下,一个好的测试方法是:

begin # Raise an error here raise "Error!!" rescue #handle the error here ensure p "=========inside ensure block" end

可以测试一下,当出现异常时,是否会打印出“==========inside Ensure block”。
然后,您可以注释掉引发错误的语句,并通过查看是否打印出任何内容来查看 
ensure

语句是否被执行。

    


5
投票
ensure

就像

finally
保证该块将被执行
。这对于确保关键资源受到保护非常有用,例如出错时关闭文件句柄,或释放互斥锁。

© www.soinside.com 2019 - 2024. All rights reserved.