61

我看到FinallyinTry .. Catch将始终在执行 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 ... 做最后的事情

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

14 回答 14

83

是的,它是不同的。finally 将始终运行(除非程序崩溃)。如果函数在 try catch 块内退出,或者在 try 或 catch 中抛出另一个错误,finally 仍将执行。如果不使用 finally 语句,您将无法获得该功能。

于 2009-07-21T11:39:02.833 回答
16

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

  • 在 TRY 中返回
  • 在 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. Return in try:在尝试中返回。最后!!
  • 如下: 3. 追赶中的回击:尝试中的投掷。...抓到!抓回来。最后!!
  • 在这里: 4. Throw in catch:在尝试中抛出。...抓到!追上来。最后!!突发新闻:发生了崩溃。

总结一下:最后需要处理两件事:

  1. 在 try 或 catch中返回的代码。
  2. 或者如果你在尝试中有异常,并且在捕获中抛出异常,
  3. 或者,如果您在尝试中有异常,并且没有捕获该异常,

最后总结“最后”:如果你尝试过,最后没有什么特别的,并且

  1. 没有回来,
  2. 并在试用期间发现任何异常,然后
  3. 也没有返回渔获物,并且
  4. 没有抛出或有抛出的代码。

最后但并非最不重要的(最后):如果您的代码中有一个您没有捕获的异常,您的代码将飞起来,而不会到达最后。

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

摩西

于 2009-07-21T13:25:45.513 回答
7

不同之处在于当try块中的代码抛出一个未被catch块捕获的异常时。

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

finally如果块 s 中的代码也将运行该tryreturn

于 2009-07-21T11:38:33.223 回答
7

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

如果不执行 finally 块,就无法退出 try 块。如果 finally 块存在,它总是执行。(此语句对于所有意图和目的都是正确的。有一种方法可以在不执行 finally 块的情况下退出 try 块。如果代码从 try 块中执行 System.exit(0); ,应用程序将在没有 finally 的情况下终止正在执行。另一方面,如果在 try 块期间拔掉机器,finally 也不会执行。)

主要用途是处理对象。当您要关闭用户定义的资源(如文件、打开的资源(db stmts))时,它将很有用。

编辑

在 stackoverflow 异常之后也不会执行 finally。

于 2009-07-21T11:38:40.603 回答
3

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

于 2009-07-21T11:39:33.580 回答
2

无论函数是否因异常而退出,Finally 块都将执行。(此规则有一些例外情况,有关更多信息,请参阅此 stackoverflow 问题)。

例如:

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 的了解还不够,无法知道这是否可行。

于 2009-07-21T11:44:42.993 回答
2

最后应该习惯于为了保持系统一致而需要完成的所有事情。这通常意味着释放资源

不管抛出什么异常,finally 总是被执行。它应该用于释放资源,在以下情况下:

  • 完成连接
  • 关闭文件处理程序
  • 空闲内存
  • 关闭数据库连接

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

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

于 2013-05-23T03:10:16.300 回答
1

您最终使用清理代码,例如数据库连接或需要关闭的打开的文件。几乎任何需要执行的清理代码,无论是否有异常

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

于 2009-07-21T11:38:47.687 回答
1

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

于 2009-07-21T11:39:37.530 回答
1

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

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

于 2009-07-21T11:41:47.603 回答
1

据我所知,我从未在我的 .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
    }
}

恕我直言,这更清洁。

于 2009-07-22T20:01:37.943 回答
0

在 try catch 块执行的任何部分之后,catch 都不会运行。只有在抛出异常并且 catch 块可以处理该类型的异常时,Catch才会运行。

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

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

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

于 2009-07-21T11:41:09.880 回答
0

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

如果不知道有问题的代码,基本上无法完全回答这个问题。

原因是并非所有代码都可以放在 finally 块中。例如,在 finally(和 catch)块中不允许使用 yield 语句。try 块可能有几个执行分支,其中一些返回而一些不返回。finally 在所有这些情况下都被执行,而在没有 finally 的示例中,清理代码不会出现这种情况。此外,您不能跳转(转到)到 finally 块,尽管非常罕见,您可以在 catch 块之后跳转到代码。您也不能从 finally 块返回。

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

于 2009-07-22T19:48:05.113 回答
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(就像我一样)。

于 2010-05-12T12:52:11.297 回答