在Ruby中开始,拯救和确保?

Llo*_*ell 528 ruby error-handling exception-handling ruby-on-rails exception

我最近开始用Ruby编程,我正在研究异常处理.

我想知道在C#中是否ensure相当于Ruby finally?我应该:

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

begin
  file << "#{content} \n"
rescue
  #handle the error here
ensure
  file.close unless file.nil?
end
Run Code Online (Sandbox Code Playgroud)

或者我应该这样做?

#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
Run Code Online (Sandbox Code Playgroud)

是否ensure被调用无论即使一个异常没有什么引发,?

Jör*_*tag 1152

是的,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
Run Code Online (Sandbox Code Playgroud)

你可以遗漏rescue,ensureelse.您也可以省略变量,在这种情况下,您将无法在异常处理代码中检查异常.(好吧,你总是可以使用全局异常变量来访问引发的最后一个异常,但这有点像hacky.)而且你可以省略异常类,在这种情况下,StandardError将捕获所有继承的异常.(请注意,这并不意味着所有的异常被捕获,因为有例外,有实例Exception,但不是StandardError.可能有非常严重的例外损害程序的完整性,例如SystemStackError,NoMemoryError,SecurityError,NotImplementedError,LoadError,SyntaxError,ScriptError,Interrupt,SignalExceptionSystemExit).

一些块形成隐式异常块.例如,方法定义也隐含地也是异常块,因此不是写入

def foo
  begin
    # ...
  rescue
    # ...
  end
end
Run Code Online (Sandbox Code Playgroud)

你写的只是

def foo
  # ...
rescue
  # ...
end
Run Code Online (Sandbox Code Playgroud)

要么

def foo
  # ...
ensure
  # ...
end
Run Code Online (Sandbox Code Playgroud)

这同样适用于class定义和module定义.

但是,在您询问的具体情况中,实际上有一个更好的习语.通常,当您使用某些需要在最后清理的资源时,可以通过将块传递给为您执行所有清理的方法来实现.它类似于usingC#中的一个块,除了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
Run Code Online (Sandbox Code Playgroud)

你知道什么:这已经在核心库中可用了File.open.但它是一种通用模式,您可以在自己的代码中使用,用于实现任何类型的资源清理(using在C#中)或事务或您可能想到的任何其他内容.

如果获取和释放资源分配到程序的不同部分,则唯一不起作用的情况.但如果它是本地化的(如您的示例中所示),则可以轻松使用这些资源块.


BTW:在现代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);
});
Run Code Online (Sandbox Code Playgroud)

  • 请注意,虽然最后执行`ensure`语句,但它们不是返回值. (78认同)
  • 我喜欢在SO上看到这样的丰富贡献.它超出了OP的要求,使其适用于更多的开发人员,但仍然是主题.我从这个答案+编辑中学到了一些东西.谢谢你不只是写"是的,`确保`无论如何都会被调用." (26认同)
  • @Teddy:确保保证开始执行,但不保证完成.你的例子是矫枉过正 - 在ensure块内部的一个简单例外也会导致它退出. (4认同)
  • 请注意,确保无法保证完成.假设你在一个线程内部有一个begin/ensure/end,然后在调用ensure块的第一行时调用Thread.kill.这将导致其余的保证不执行. (3认同)
  • 还要注意,没有保证确保被调用.我是认真的.可能会发生停电/硬件错误/操作系统崩溃,如果您的软件很关键,则需要考虑这一点. (3认同)
  • 请注意,C#示例并未消除对using的需要。open方法仍然需要进行清理。该示例仅以冗长的方式(而不是100%的防弹方式)执行此操作,而不是使用“ using”速记。我建议尽可能使用`代替`try-finally`。 (2认同)
  • 我不知道说“使用”是“多余的”是没有道理的。整个要点是写出try的更好方法-最终阻止您编写的代码。 (2认同)
  • 哦。好吧,在我看来,这似乎比尝试解决传统C#模型无法解决的实际问题更像是尝试用C#编写Ruby。 (2认同)
  • 但是,如果您想要,ensure 语句可以返回一个值,如 `def test begin 144/0 saving StandardError =&gt;e 77 Ensure put "in Ensure" return 66 end end` ,这将返回 `66` 而不是 `77`,而不需要`return 66` 该方法返回 `77` (2认同)

小智 34

仅供参考,即使在该rescue部分中重新引发异常,该ensure块也将在代码执行继续执行到下一个异常处理程序之前执行.例如:

begin
  raise "Error!!"
rescue
  puts "test1"
  raise # Reraise exception
ensure
  puts "Ensure block"
end
Run Code Online (Sandbox Code Playgroud)


Far*_*rel 14

如果要确保文件已关闭,则应使用以下块形式File.open:

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

  • 我想如果你不想处理错误但只是提高它,并关闭文件句柄,你不需要在这里开始救援? (3认同)

Mil*_*ota 7

是的,ensure在任何情况下都会被调用.有关更多信息,请参阅编程Ruby书籍的" 异常,捕获和抛出 "并搜索"确保".


kub*_*oon 6

这就是为什么我们需要ensure

def hoge
  begin
    raise
  rescue  
    raise # raise again
  ensure  
    puts 'ensure' # will be executed
  end  
  puts 'end of func' # never be executed
end  
Run Code Online (Sandbox Code Playgroud)


Chr*_*ley 5

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


Aar*_*ian 5

是的,ensure确保每次都运行它,因此您无需file.close在代码begin块中使用。

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

begin
  # Raise an error here
  raise "Error!!"
rescue
  #handle the error here
ensure
  p "=========inside ensure block"
end
Run Code Online (Sandbox Code Playgroud)

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