我见过有人说,使用不带参数的catch是一种糟糕的形式,尤其是当catch什么都不做的时候:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

然而,这被认为是良好的形式:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

据我所知,将清理代码放在finally块和将清理代码放在try. catch块之后的唯一区别是,如果你在try块中有返回语句(在这种情况下,finally中的清理代码将运行,但try. catch块之后的代码将不会运行)。

否则,最后有什么特别的?


当前回答

虽然下面两个代码块是等效的,但它们并不相等。

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}

'finally'是意图揭示代码。您向编译器和其他程序员声明,无论如何都需要运行这段代码。 如果您有多个捕获块,并且有清理代码,那么您最终需要。如果没有最后,您将在每个catch块中重复清理代码。(干燥原理)

最后,积木是特殊的。CLR将finally块中的代码与catch块分开来识别和处理,并且CLR竭尽全力确保finally块始终执行。这不仅仅是编译器的语法糖。

其他回答

最后无论如何都会执行。如果你的try块成功,它就会执行,如果你的try块失败,它就会执行catch块,然后是finally块。

此外,最好尝试使用以下结构:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

由于using语句被自动包装在try / finally语句中,流将自动关闭。(如果想要真正捕获异常,则需要在using语句周围加上try / catch)。

从可读性的角度来看,它更明确地告诉未来的代码读者“这里的东西很重要,无论发生什么都需要完成。”这很好。

此外,空catch语句对它们来说往往有某种“味道”。它们可能表明开发人员没有仔细考虑可能发生的各种异常以及如何处理它们。

只要不抛出异常,示例之间的有效差异就可以忽略不计。

但是,如果在'try'子句中抛出异常,则第一个示例将完全忽略它。第二个示例将向调用堆栈的下一个步骤抛出异常,因此所述示例的区别在于,一个示例完全掩盖了任何异常(第一个示例),而另一个示例(第二个示例)保留了异常信息,以便后续处理,同时仍然执行'finally'子句中的内容。

例如,如果您将代码放在第一个示例的'catch'子句中,该子句抛出异常(无论是最初引发的异常还是新抛出的异常),则阅读器清理代码永远不会执行。最后执行,不管'catch'子句中发生了什么。

因此,'catch'和'finally'之间的主要区别是,'finally'块的内容(少数例外)可以被认为是保证执行的,即使面对意外的异常,而'catch'子句之后的任何代码(但在'finally'子句之外)都不会带有这样的保证。

顺便说一句,Stream和StreamReader都实现了IDisposable,并且可以包装在一个“using”块中。'Using'块在语义上等同于try/finally(没有'catch'),所以你的例子可以更简洁地表达为:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

...它会在StreamReader实例超出作用域时关闭并销毁它。 希望这能有所帮助。

如果您不知道要捕获什么异常类型或如何处理它,那么使用catch语句是没有意义的。你应该把它留给上级,他们可能对情况有更多的了解,知道该怎么做。

在那里仍然应该有一个finally语句,以防出现异常,这样就可以在异常抛出给调用者之前清理资源。

最大的区别是尝试…Catch将吞下异常,隐藏发生错误的事实。Try .finally将运行您的清理代码,然后异常将继续运行,由知道如何处理它的程序来处理。