我想知道 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
也会被调用吗?
是的,
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
)或事务或您可能想到的任何其他内容。如果获取和释放资源分布在程序的不同部分,则此方法不起作用。但如果它是本地化的,如您的示例所示,那么您可以轻松使用这些资源块。
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);
});
rescue
部分重新引发异常,
ensure
块也会在代码执行继续到下一个异常处理程序之前执行。例如:begin
raise "Error!!"
rescue
puts "test1"
raise # Reraise exception
ensure
puts "Ensure block"
end
File.open
的块形式:
File.open("myFile.txt", "w") do |file|
begin
file << "#{content} \n"
rescue
#handle the error here
end
end
ensure
的原因:
def hoge
begin
raise
rescue
raise # raise again
ensure
puts 'ensure' # will be executed
end
puts 'end of func' # never be executed
end
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
语句是否被执行。
ensure
就像
finally
保证该块将被执行。这对于确保关键资源受到保护非常有用,例如出错时关闭文件句柄,或释放互斥锁。