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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2191632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:14:15  来源:igfitidea点击:

Begin, Rescue and Ensure in Ruby?

ruby-on-railsrubyexceptionexception-handlingerror-handling

提问by Lloyd Powell

I've recently started programming in Ruby, and I am looking at exception handling.

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

I was wondering if ensurewas the Ruby equivalent of finallyin C#? Should I have:

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

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

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

or should I do this?

还是我应该这样做?

#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

Does ensureget called no matter what, even if an exception isn't raised?

不会ensure得到所谓不管,即使一个异常没有什么引发,?

回答by J?rg W Mittag

Yes, ensureensures that the code is always evaluated. That's why it's called ensure. So, it is equivalent to Java's and C#'s finally.

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

The general flow of begin/rescue/else/ensure/endlooks like this:

的一般流程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

You can leave out rescue, ensureor else. You can also leave out the variables in which case you won't be able to inspect the exception in your exception handling code. (Well, you can always use the global exception variable to access the last exception that was raised, but that's a little bit hacky.) And you can leave out the exception class, in which case all exceptions that inherit from StandardErrorwill be caught. (Please note that this does not mean that allexceptions are caught, because there are exceptions which are instances of Exceptionbut not StandardError. Mostly very severe exceptions that compromise the integrity of the program such as SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt, SignalExceptionor SystemExit.)

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

Some blocks form implicit exception blocks. For example, method definitions are implicitly also exception blocks, so instead of writing

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

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

you write just

你只写

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

or

或者

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

The same applies to classdefinitions and moduledefinitions.

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

However, in the specific case you are asking about, there is actually a much better idiom. In general, when you work with some resource which you need to clean up at the end, you do that by passing a block to a method which does all the cleanup for you. It's similar to a usingblock in C#, except that Ruby is actually powerful enough that you don't have to wait for the high priests of Microsoft to come down from the mountain and graciously change their compiler for you. In Ruby, you can just implement it yourself:

但是,在您询问的特定情况下,实际上有更好的习语。一般来说,当您使用一些需要在最后清理的资源时,您可以通过将一个块传递给一个为您完成所有清理工作的方法来完成此操作。它类似于usingC# 中的一个块,除了 Ruby 实际上足够强大,您不必等待 Microsoft 的大祭司下山并慷慨地为您更改他们的编译器。在 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

And what do you know: this is alreadyavailable in the core library as File.open. But it is a general pattern that you can use in your own code as well, for implementing any kind of resource cleanup (à la usingin C#) or transactions or whatever else you might think of.

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

The only case where this doesn't work, if acquiring and releasing the resource are distributed over different parts of the program. But if it is localized, as in your example, then you can easily use these resource blocks.

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



BTW: in modern C#, usingis actually superfluous, because you can implement Ruby-style resource blocks yourself:

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);
});

回答by alup

FYI, even if an exception is re-raised in the rescuesection, the ensureblock will be executed before the code execution continues to the next exception handler. For instance:

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

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

回答by Farrel

If you want to ensure a file is closed you should use the block form of File.open:

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

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

回答by Milan Novota

Yes, ensureis called in any circumstances. For more information see "Exceptions, Catch, and Throw" of the Programming Ruby book and search for "ensure".

是的,ensure在任何情况下都会调用。有关更多信息,请参阅Programming Ruby 书籍的“ Exceptions, Catch, and Throw”并搜索“ensure”。

回答by Aaron Qian

Yes, ensureENSURES it is run every time, so you don't need the file.closein the beginblock.

是的,ensure确保它每次都运行,所以你不需要file.closebegin块中。

By the way, a good way to test is to do:

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

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

You can test to see if "=========inside ensure block" will be printed out when there is an exception. Then you can comment out the statement that raises the error and see if the ensurestatement is executed by seeing if anything gets printed out.

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

回答by kuboon

This is why we need ensure:

这就是为什么我们需要ensure

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

回答by Chris McCauley

Yes, ensurelike finallyguarantees that the block will be executed. This is very useful for making sure that critical resources are protected e.g. closing a file handle on error, or releasing a mutex.

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