开始,抢救和确保在Ruby?

我最近开始用Ruby进行编程,并且正在寻找exception处理。

我想知道,如果ensure是在C#中finally的Ruby等价物? 我应该有:

 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 

即使没有引发exception, ensure被调用了吗?

是的, ensure确保代码始终得到评估。 这就是为什么要ensure 。 所以最后等于Java和C#的。

begin / rescue / else / ensure / end的一般stream程如下所示:

 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 。 您也可以省略variables,在这种情况下,您将无法在exception处理代码中检查exception。 (嗯,你总是可以使用全局的exceptionvariables来访问最后一个引发的exception,但这有些冒险。)你可以省去exception类,在这种情况下,所有从StandardErrorinheritance的exception都会被捕获。 (请注意,这并不意味着所有的exception都会被捕获,因为有一些exception是Exception例子,而不是StandardError 。大多数非常严重的exception会影响程序的完整性,比如SystemStackErrorNoMemoryErrorSecurityErrorNotImplementedErrorLoadErrorSyntaxErrorScriptErrorInterruptSignalExceptionSystemExit 。)

一些块形成隐含的exception块。 例如,方法定义也是隐含的exception块,所以不用写

 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 unless filehandle.nil? end 

你知道什么:这已经在核心库中作为File.open 。 但是这是一种通用模式,您可以在自己的代码中使用它来实现任何types的资源清理( using C#)或事务或任何您可能会想到的事情。

唯一的情况下,这是行不通的,如果获取和释放资源分布在程序的不同部分。 但是如果它是本地化的,就像你的例子那样,那么你可以很容易地使用这些资源块。


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

仅供参考,即使在rescue部分重新生成exception,在代码执行继续到下一个exception处理程序之前,将执行ensure块。 例如:

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

如果你想确保一个文件被closures,你应该使用File.open的块forms:

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

是的, ensure在任何情况下被调用。 有关更多信息,请参阅Programming Ruby book的“ Exceptions,Catch和Throw ”,并search“确保”。

是的, ensure ENSURES每次都运行,所以你不需要在begin块中使用file.close

顺便说一下,一个好的testing方法是:

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

当出现exception时,您可以testing是否打印出“========= inside ensure block”。 然后,您可以注释掉引发错误的语句,看看是否通过查看是否打印出了ensure语句。

是的, ensurefinally 保证块将被执行 。 这对于确保关键资源受到保护非常有用,例如closures错误的文件句柄或释放互斥锁。

这就是为什么我们需要ensure

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