开始,抢救和确保在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
你可以省去rescue
, ensure
或else
。 您也可以省略variables,在这种情况下,您将无法在exception处理代码中检查exception。 (嗯,你总是可以使用全局的exceptionvariables来访问最后一个引发的exception,但这有些冒险。)你可以省去exception类,在这种情况下,所有从StandardError
inheritance的exception都会被捕获。 (请注意,这并不意味着所有的exception都会被捕获,因为有一些exception是Exception
例子,而不是StandardError
。大多数非常严重的exception会影响程序的完整性,比如SystemStackError
, NoMemoryError
, SecurityError
, NotImplementedError
, LoadError
, SyntaxError
, ScriptError
, Interrupt
, SignalException
或SystemExit
。)
一些块形成隐含的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
语句。
是的, ensure
像finally
保证块将被执行 。 这对于确保关键资源受到保护非常有用,例如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