为啥在 Try ... Catch 中使用 finally
Posted
技术标签:
【中文标题】为啥在 Try ... Catch 中使用 finally【英文标题】:Why use Finally in Try ... Catch为什么在 Try ... Catch 中使用 finally 【发布时间】:2010-11-12 14:53:30 【问题描述】:我看到Try .. Catch
中的Finally
将始终在try catch 块执行的任何部分之后执行。
跳过 Finally
部分然后在 try catch 块之外运行它有什么不同吗?
示例 1,Try ... Catch ... Final ... End Try
> Try
'Do something
Catch ex As Exception
'Handle exception
Finally
'Do cleanup
End Try
示例 2,Try ... Catch ... End Try ... 在外面做 finally 的东西
Try
'Do something
Catch ex As Exception
'Handle exception
End Try
'Do cleanup
【问题讨论】:
【参考方案1】:最后应该习惯于为了保持系统的一致性而需要做的所有事情。这通常意味着释放资源
无论抛出什么异常,Final 总是被执行。它应该用于释放资源,在以下情况下:
完成连接 关闭文件处理程序 空闲内存 关闭数据库连接让我举一个完整的例子。想象一下,您正在通过网络发送消息。在伪代码中:
// With finally | //Without finally
try | try
send_message() | send_message()
catch(NetworkError) | catch(NetworkError)
deal_with_exception() | deal_with_exception()
finally |
finalizes_connection() | finalizes_connection()
|
这两个代码的唯一区别是当 try
块中的内容引发不是 NetworkError
的异常时,例如 MethodNotFound
。第一种情况会调用方法finalizes_connection()
,而第二种情况不会。
连接自然是通过多个程序完成的。那么在另一个程序出现MethodNotFound
异常的情况下会发生什么?在第一种情况下,您的程序将完成连接和另一个程序,它会很高兴。在第二种情况下,另一个程序可能会永远等待您的响应。如果另一个程序每次只能接收一个连接怎么办?你刚刚也窃听了另一个程序。
这也适用于文件,例如,您打开而其他程序将无法打开以供阅读(在 Windows 中)。而对于内存,它永远不会被释放,现在你有内存泄漏。
【讨论】:
如果您只捕获特定的异常类型,这很明显,但在我的示例中,我捕获了Exception
类型的异常,这实际上是 any 的异常 类型。那么finally
的需求就不是很明显了,不过这个别人回答的很好。【参考方案2】:
我经常使用的是(我把它封装成一个方面,但这是我派生的方面):
public static DoLengthyProcessing(this Control control, Action<Control> action)
Cursor oldCursor = control.Cursor
try
control.Cursor = Cursors.WaitCursor;
action(control);
catch (Exception ex)
ErrorHandler.Current.Handler(ex);
finally
control.Cursor = oldCursor;
或者,使用 AOP(就像我一样)。
【讨论】:
【参考方案3】:无论函数是否因异常而退出,Finally 块都会执行。 (此规则有一些例外情况,see this *** question 了解更多信息)。
例如:
Try
'Do something
Catch ex As Exception
if 'Some Condition
throw ex
else
'Handle exception
Finally
'Do cleanup
End Try
在这种情况下,Finally 块仍将执行,即使您可能会从函数中抛出异常。
这是一个很好的实践,因为它可以确保您的清理代码始终执行。当然,使用Resoource Acquisition Is Initialization 成语是确保资源得到清理的一种更清洁的方法,但我对 VB.net 的了解不够,不知道这是否可行。
【讨论】:
“总是”? finally 块无法运行的原因有很多。 @Nick:请注意,finally 块并非保证始终运行;此规则有一些例外(!); ***Exception 就是其中之一。 在try块中间拔掉电脑也会导致finally不执行:) 如果执行到达外部范围,则所有内部 finally 块都保证已“运行”[尽管有些可能由于异常而提前退出]。请注意,在 vb.net 或 CIL 中,外部try
块可能包含将从嵌套 finally 块的上下文中运行的代码,尽管 C# 不公开此类功能。至于 RAII,vb.net 和 C# 可以通过 using
构造将该概念应用于局部变量,但不能将其有效地应用于字段。
他们确实应该添加一个 PlugPulled 异常。还有一个 NotPluggedIn 异常,只是为了处理所有可能发生的情况。 *** 异常当然应该重定向到这个站点。【参考方案4】:
据我所知,我从未在我的 .NET 代码中使用过 try/catch/finally 块。
一般来说,很少需要在中间层捕获异常。异常通常传播到表示层中的***处理程序(并且可能在层边界处捕获并重新抛出,以便记录它们)。
因此,在中间层,您会更频繁地看到 try/finally(或“使用”语句),以便清理资源。并在表示层的***处理程序中的 try/catch 中。
在我既需要捕获异常又需要进行一些清理的极少数情况下,我更愿意进行重构,以便以下内容:
try
... do something
catch
... handle exception
finally
... cleanup
变成:
try
DoSomethingAndCleanup();
catch
... handle exception
...
private void DoSomethingAndCleanup()
try
... do something
finally
... cleanup
恕我直言,这更干净。
【讨论】:
如果原始异常是由在内部代码中确实有意义但在表示层中没有意义的东西引起的,则中间层中的 Cathcing 异常是有意义的。然后抛出一个异常来提供有关实际错误的上下文的更多详细信息是有意义的。 太糟糕了,没有解决异常的借口,没有惯用的方法来响应异常。方法将对象暂时置于损坏状态但然后在它们返回之前修复它是很常见的。此类代码应由try
块保护,如果发生意外异常,该块将明确使对象无效; catch
似乎很恶心,一个不希望解决的异常,但我不确定 C# 中会存在什么替代方案。【参考方案5】:
看了上面对我评论的回复后,我想到了一些事情。
如果不知道问题的代码,这个问题基本上不能完全回答。
原因是并非所有代码都可以放在 finally 块中。例如。在 finally(和 catch)块中不允许使用 yield 语句。 try 块可能有几个执行分支,其中一些返回而一些不返回。 finally 在所有这些情况下都被执行,而在没有 finally 的示例中,清理代码不会出现这种情况。此外,您不能跳转(转到)到 finally 块,尽管非常罕见,您可以在 catch 块之后跳转到代码。你也不能从 finally 块返回。
在很多情况下,答案取决于实际代码。
【讨论】:
我同意不是所有在 try 块之后执行的代码都应该在 finally 块中。我的问题主要是为了澄清技术差异是什么,这在此处的最佳答案中得到了很好的回答。【参考方案6】:带有四个单选按钮的代码:
尝试返回 在 CATCH 中返回 投掷 CATCH完成捕捉
private void checkFinally()
try
doFinally();
catch
Console.WriteLine(" Breaking news: a crash occured. ");
private void doFinally()
Console.WriteLine(" ");
Console.Write("Here goes: "
+ (radioReturnInTry.Checked ? "2. Return in try: "
: (radioReturnInCatch.Checked? "3. Retrun in catch: "
: (radioThrowInCatch.Checked? "4. Throw in catch: "
: "1. Continue in catch: "))) );
try
if (radioReturnInTry.Checked)
Console.Write(" Returning in try. ");
return;
Console.Write(" Throwing up in try. ");
throw new Exception("check your checkbox.");
catch (Exception ex)
Console.Write(" ...caughtcha! ");
if (radioReturnInCatch.Checked)
Console.Write("Returning in catch. ");
return;
if (radioThrowInCatch.Checked)
Console.Write(" Throwing up in catch. ");
throw new Exception("after caught");
finally Console.Write(" Finally!!");
Console.WriteLine(" Done!!!"); // before adding checkboxThrowInCatch,
// this would never happen (and was marked grey by ReSharper)
输出:
如下: 1. 继续捕获:尝试抛出。 ...抓到!最后!!完成!!! 这里是: 2. 在尝试中返回:在尝试中返回。终于!! 如下: 3. 在捕获中返回:在尝试中抛出。 ...抓到!抓回来。最后!! 在这里: 4. 投掷:投掷尝试。 ...抓到!追上来。最后!!突发新闻:发生了崩溃。总结一下: 最后处理两件事:
-
在 try 或 catch 中返回的代码。
或者如果你在 try 中有异常,并且在 catch 中抛出异常,
或者,如果您在尝试中遇到异常,并且没有捕获到该异常,
最后总结一下“FINALLY”:最后如果你尝试过并没有什么特别的,并且
-
没有返回,
并在试用期间发现任何异常,然后
也没有在捕获中返回,并且
没有抛出或有抛出的代码。
最后但并非最不重要的(最后): 如果你的代码中有一个你没有捕捉到的异常,你的代码会飞起来,而不会到达最后。
希望这很清楚。 (现在是我...)
莫舍
【讨论】:
懒得阅读和比较条件。要是有人能把它转换成表格就好了!!【参考方案7】:除了其他人所说的之外,从语义上讲,我认为它们是不同的。
finally 块中的代码清楚地表明您正在为 try-catch 中包含的内容执行终结类型的任务。我认为这使阅读更清晰。
【讨论】:
我完全同意你的看法。我只是好奇这是否有任何技术差异,根据其他人的说法,它是......【参考方案8】:最后包含需要在所有条件下评估的代码[无论是否发生异常]。
如果不执行 finally 块,就无法退出 try 块。如果 finally 块存在,它总是执行。 (此语句适用于所有意图和目的。有一种方法可以在不执行 finally 块的情况下退出 try 块。如果代码从 try 块中执行 System.exit(0); ,则应用程序将在没有 finally 的情况下终止另一方面,如果在 try 块期间拔掉机器,finally 也不会执行。)
主要用于处理对象。当你想关闭用户时它会很有用 定义的资源,如文件,打开的资源(db stmts)。
编辑
在 *** 异常之后也不会执行 finally。
【讨论】:
如果出现 ***Exception 是否会执行 finally 块? 这假定进程不会突然终止。【参考方案9】:Catch 将不在 try catch 块执行的任何部分之后运行。 Catch 仅在抛出异常并且 catch 块可以处理该类型的异常时运行。
finally 块是在 try 块完成时运行的块。完成,我的意思是它正常完成,或者以某种方式退出(跳出循环,从方法返回,抛出异常等)
如果将代码放在 finally 块之外并抛出异常(例如),则如果未捕获异常,则代码可能不会执行,或者在 catch 块中重新抛出,或者抛出新的异常在 catch 块中。如果你把它放在 finally 块中,它就会被执行。
基本上,清理应该放在 finally 块中。
【讨论】:
【参考方案10】:在 finally 块中进行清理是为了确保它运行。如果 catch 块不处理异常(即它只是记录它),甚至导致另一个异常,finally 块中的代码仍然会运行。
【讨论】:
【参考方案11】:在处理数据库连接或任何时候需要处理对象时,这是一个好主意。万一在运行查询时出现问题,您仍然可以安全地关闭连接。它还有助于清理 try/catch/finally 块之外的块无法访问的代码。
【讨论】:
【参考方案12】:是的,它是不同的。 finally 将始终运行(除非程序崩溃)。如果函数在 try catch 块内退出,或者在 try 或 catch 中抛出另一个错误,finally 仍将执行。如果不使用 finally 语句,您将无法获得该功能。
【讨论】:
不幸的是,finally
块运行默认如果程序以各种方式崩溃。但如果你愿意,你可以覆盖它:处理 AppDomain.UnhandledException
以调用 Environment.FailFast
。
虽然如果你是一名 Java 程序员,那你就倒霉了:未处理的异常会执行 finally
块,而你真的无能为力。
好吧,你可以做点什么,你可以设计/编写你的代码,这样它就不会使用异常来进行流控制,也不要将 finally 块用于类似的事情。 finally 应该用于清理,这应该总是发生。
不是真的,有时 finally 可能不会被调用。就像从操作系统中止程序或计算机崩溃一样。
它不会执行的两种方式是当执行try/catch的线程停止执行或JVM终止时。所以线程可能会停止,程序的其余部分会继续,你永远不会知道(除非有好的日志/编码)它是否运行。【参考方案13】:
finally 用于清理代码,例如数据库连接或打开但需要关闭的文件。几乎所有需要执行的清理代码,无论是否出现异常
另外,您的异常处理可能需要重新抛出异常或其他异常,在这种情况下,块之后的代码将不会被执行
【讨论】:
【参考方案14】:不同之处在于try
块中的代码会引发catch
块未捕获的异常。
通常catch
块会捕获特定类型的异常,并让其他任何东西通过。在这种情况下,finally
块仍将运行。
如果try
块中的代码return
s 中的代码,finally
块也将运行。
【讨论】:
异常是否被捕获并不重要。 finally 块始终运行(排除异常情况,例如“蛮力”应用程序退出) @Rune FS:是的,但是如果捕获到异常,则 awe 的两个示例之间没有区别(除非引发更多异常,return
语句等)。如果没有被抓到,那就有的区别了。这就是敬畏所要问的。以上是关于为啥在 Try ... Catch 中使用 finally的主要内容,如果未能解决你的问题,请参考以下文章