小编典典

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

all

我最近开始使用 Ruby 进行编程,并且正在研究异常处理。

我想知道ensureRuby 是否相当于finallyC# 中的?我应该有:

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无论如何都会被调用,即使没有引发异常?


阅读 116

收藏
2022-03-06

共1个答案

小编典典

ensure的,确保始终评估代码。这就是它被称为 的原因ensure。因此,它相当于 Java 和 C# 的finally.

////的大致流程是这样的beginrescue``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

您可以省略rescueensureelse。您还可以省略变量,在这种情况下您将无法在异常处理代码中检查异常。(好吧,您总是可以使用全局异常变量来访问引发的最后一个异常,但这有点笨拙。)您可以省略异常类,在这种情况下,所有继承自的异常都StandardError将被捕获。(请注意,这并不意味着
所有 异常都被捕获,因为有些异常是
的实例,Exception但不是StandardError。大多数非常严重的异常会损害程序的完整性,例如SystemStackError,
NoMemoryError, SecurityError, NotImplementedError, LoadError,
SyntaxError, ScriptError, Interrupt,SignalExceptionSystemExit。)

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

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

你只写

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

要么

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

这同样适用于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

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

如果获取和释放资源分布在程序的不同部分,那么这不起作用的唯一情况。但是,如果它是本地化的,如您的示例中那样,那么您可以轻松地使用这些资源块。


顺便说一句:在现代 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);
});
2022-03-06