为什么在Try ... Catch中使用Finally

时间:2009-07-21 11:35:46

标签: .net vb.net exception-handling try-catch-finally

我看到Finally中的Try .. Catch将始终在执行try catch块的任何部分后执行。

只是跳过Finally部分并在try catch块之外运行它会有什么不同吗?

示例1,尝试......抓住......最后......结束尝试

    Try
        'Do something
    Catch ex As Exception
        'Handle exception
    Finally
        'Do cleanup
    End Try

示例2,尝试......抓住......结束尝试......做最后的事情

    Try
        'Do something
    Catch ex As Exception
        'Handle exception
    End Try
    'Do cleanup

14 个答案:

答案 0 :(得分:65)

是的,情况有所不同。最后将始终运行(禁止程序崩溃)。如果函数退出try catch块内部,或者在try或catch中抛出另一个错误,则finally仍将执行。您不会使用finally语句获得该功能。

答案 1 :(得分:12)

带有四个单选按钮的代码:

  • 以TRY返回
  • 返回CATCH
  • 扔进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。继续捕捉:尝试投掷。 ... caughtcha!最后!!完成!!!
  • 这里是:2。回归尝试:回归尝试。最后!!
  • 这里有:3。重新捕获:尝试投掷。 ... caughtcha!回归陷阱。最后!!
  • 这里是:4。抓住:抓住尝试。 ... caughtcha!陷入困境。最后!!突发新闻:发生了崩溃。

总结一下: 最后需要注意两件事:

  1. 在try或catch中返回的代码。
  2. 或者如果你在try中有异常,并且在catch中有一个异常,
  3. 或者,如果您在try中有异常,并且DID NOT CATCH该异常,
  4. 最后总结一下“最后”最后如果你尝试过没有什么特别的,

    1. DID NOT RETURN,
    2. 并在试用期间发现了任何例外,然后
    3. 也没有退回,并且
    4. 不要吝啬或有代码抛出。
    5. 最后但并非最不重要(最后):   如果你的代码中有一个你没有捕获的例外,那么你的代码将会飞,而不会达到最终状态。

      希望这很清楚。 (现在是给我的......)

      摩西

答案 2 :(得分:7)

最后包含需要在所有条件下评估的代码[无论是否发生异常]。

如果不执行finally块,则无法退出try块。如果finally块存在,则始终执行。 (这个说法适用于所有意图和目的。有一种方法可以在不执行finally块的情况下退出try块。如果代码执行System.exit(0);从try块中,应用程序终止而不是finally另一方面,如果你在try块期间拔掉机器,那么finally也不会执行。)

主要用途是处理物体。当您想要关闭用户时,它会很有用 已定义的资源,如文件,已打开的资源(db stmts)。

<强> 修改

最后也不会在stackoverflow异常后执行。

答案 3 :(得分:4)

区别在于try块中的代码抛出了catch块未捕获的异常。

通常,catch块会捕获特定类型的异常,并让其他任何异常通过。在这种情况下,finally块仍将运行。

如果finally阻止try s中的代码,return块也会运行。

答案 4 :(得分:3)

在处理数据库连接或任何需要处理的对象时,这是一个好主意。为了防止在运行查询时出现问题,您仍然可以安全地关闭连接。它还有助于清理try / catch / finally块外部的块无法访问的代码。

答案 5 :(得分:2)

无论函数是否因异常退出,都将执行Finally块。 (此规则有一些例外,see this stackoverflow 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中不够熟悉这是否可行。

答案 6 :(得分:2)

最后应该用于需要完成的所有事情,以保持系统的一致性。这通常意味着发布资源

无论抛出什么异常,最终都会被执行。在以下情况下,它应该用于释放资源:

  • 完成连接
  • 关闭文件处理程序
  • 免费记忆
  • 关闭数据库连接

让我举一个完整的例子。想象一下,您正在通过网络发送消息。在伪代码中:

// 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中)。对于内存,它永远不会被释放,现在你有内存泄漏。

答案 7 :(得分:1)

最后使用清理代码,例如数据库连接或需要关闭的打开的文件。几乎所有需要执行的清理代码都与

无关

另外,您的异常处理可能需要重新抛出异常或其他异常,在这种情况下,块之后的代码将不会被执行

答案 8 :(得分:1)

在finally块中进行清理是为了确保它运行。如果catch块没有处理异常(即它只是记录它),甚至导致另一个异常,那么finally块中的代码仍会运行。

答案 9 :(得分:1)

除了其他人所说的,语义上我认为他们是不同的。

finally块中的代码清楚地表明您正在为try-catch中包含的内容执行最终类型任务。我认为这使得阅读更加清晰。

答案 10 :(得分:1)

据我所知,我从未在.NET代码中使用过try / catch / finally块。

通常,很少需要捕获中间层中的异常。异常通常传播到表示层中的顶级处理程序(并且可能在层边界捕获并重新抛出,因此可以记录它们)。

因此,在中间层,您将经常看到try / finally(或“using”语句),以便清理资源。并且在表示层中的顶级处理程序中的try / catch中。

在极少数情况下,我需要捕获异常并进行一些清理,我宁愿重构以便以下内容:

try
{
    ... do something
}
catch
{
   ... handle exception
}
finally
{
   ... cleanup
}

变为:

try
{
    DoSomethingAndCleanup();
}
catch
{
   ... handle exception
}

...
private void DoSomethingAndCleanup()
{
    try
    {
        ... do something
    }
    finally
    {
        ... cleanup
    }
}

恕我直言,这更清洁了。

答案 11 :(得分:0)

在执行try catch块的任何部分后,Catch将运行。如果抛出异常并且catch块可以处理该类型的异常,Catch将运行。

finally块是try块完成时运行的块。完成后,我的意思是它正常完成,或以某种方式退出(中断循环,从方法返回,抛出异常等)。

如果将代码放在finally块之外并抛出异常(例如),则如果未捕获异常,或者在catch块中重新抛出异常,或者抛出新异常,则可能无法执行代码在拦截区。如果你将它放在finally块中,那么它将被执行。

基本上,清理工作应放在finally块中。

答案 12 :(得分:0)

在阅读了上述评论的回复之后,我想到了一些事情。

在了解相关代码的情况下,基本上无法完全回答这个问题。

原因是并非所有代码都可以放在finally块中。例如。 finally(和catch)块中不允许使用yield语句。 try块可能有几个执行分支,其中一些返回,一些不返回。最终在所有这些情况下执行,而在示例中没有最终不会是清理代码的情况。更多的你不能跳转(转到)finally块,虽然非常罕见,你可以跳转到catch块之后的代码。你不能从finally块返回。

虽然最常见的情况是答案取决于实际代码,但有很多非常罕见的情况。

答案 13 :(得分:0)

我经常使用的是(我把它封装成一个方面,但这是我得出的方面):

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(和我一样)。