[英]Begin, Rescue and Ensure in Ruby?
I've recently started programming in Ruby, and I am looking at exception handling.我最近开始用 Ruby 编程,我正在研究异常处理。
I was wondering if ensure
was the Ruby equivalent of finally
in C#?我想知道在 C# 中
ensure
是不是 Ruby 中的finally
等价物? Should I have:我应该:
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 ensure
get called no matter what, even if an exception isn't raised?即使没有引发异常,
ensure
无论如何都会被调用吗?
Yes, ensure
ensures that the code is always evaluated.是的,
ensure
始终评估代码。 That's why it's called ensure
.这就是为什么它被称为
ensure
。 So, it is equivalent to Java's and C#'s finally
.因此,它相当于 Java 和 C# 的
finally
。
The general flow of begin
/ rescue
/ else
/ ensure
/ end
looks 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
, ensure
or else
.您可以省略
rescue
、 ensure
或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
StandardError
will be caught. (好吧,你总是可以使用全局异常变量来访问最后一个引发的异常,但这有点hacky。)你可以省略异常类,在这种情况下,所有继承自
StandardError
异常都将被捕获。 (Please note that this does not mean that all exceptions are caught, because there are exceptions which are instances of Exception
but not StandardError
. Mostly very severe exceptions that compromise the integrity of the program such as SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
, SignalException
or SystemExit
.) (请注意,这并不意味着所有异常都被捕获,因为有些异常是
Exception
实例而不是StandardError
。大多数是危及程序完整性的非常严重的异常,例如SystemStackError
、 NoMemoryError
、 SecurityError
、 NotImplementedError
、 LoadError
, SyntaxError
、 ScriptError
、 Interrupt
、 SignalException
或SystemExit
。)
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 class
definitions and module
definitions.这同样适用于
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
using
block 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.它类似于 C# 中的
using
块,不同之处在于 Ruby 实际上足够强大,您不必等待 Microsoft 的高手下山并慷慨地为您更改他们的编译器。 In Ruby, you can just implement it yourself:在 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 already available in the core library as File.open
.你知道什么:这已经在核心库中以
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 using
in C#) or transactions or whatever else you might think of.但它是一种普遍现象,你可以在自己的代码使用,以及用于实现任何类型的资源清理的(点菜
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#, using
is actually superfluous, because you can implement Ruby-style resource blocks yourself:顺便说一句:在现代 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);
});
FYI, even if an exception is re-raised in the rescue
section, the ensure
block will be executed before the code execution continues to the next exception handler.仅供参考,即使在
rescue
部分重新引发异常,也将在代码执行继续到下一个异常处理程序之前执行ensure
块。 For instance:例如:
begin
raise "Error!!"
rescue
puts "test1"
raise # Reraise exception
ensure
puts "Ensure block"
end
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
Yes, ensure
is called in any circumstances.是的,
ensure
在任何情况下都会被调用。 For more information see " Exceptions, Catch, and Throw " of the Programming Ruby book and search for "ensure".有关更多信息,请参阅 Programming Ruby 书籍的“ Exceptions, Catch, and Throw ”并搜索“ensure”。
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
Yes, ensure
like finally
guarantees that the block will be executed .
是的,
ensure
像finally
保证块将被执行。 This is very useful for making sure that critical resources are protected eg closing a file handle on error, or releasing a mutex.这对于确保关键资源受到保护非常有用,例如在出错时关闭文件句柄或释放互斥锁。
Yes, ensure
ENSURES it is run every time, so you don't need the file.close
in the begin
block.是的,
ensure
每次都运行它,因此您不需要begin
块中的file.close
。
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.您可以测试一下,当出现异常时,是否会打印出“==========inside ensure block”。 Then you can comment out the statement that raises the error and see if the
ensure
statement is executed by seeing if anything gets printed out.然后,您可以注释掉引发错误的语句,并通过查看是否打印出任何内容来查看是否执行了
ensure
语句。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.