关于.net:finally块有什么意义?

关于.net:finally块有什么意义?

What is the point of the finally block?

除了语法,两者之间有什么区别

1
2
3
4
5
6
7
try {
}
catch() {
}
finally {
    x = 3;
}

1
2
3
4
5
6
try {
}
catch() {
}

x = 3;

编辑:.NET 2.0中?

所以

1
2
3
4
5
6
7
try {
    throw something maybe
    x = 3
}
catch (...) {
    x = 3
}

在行为上是等效的?


好吧,一方面,如果您在try块中返回,则finally仍将运行,但是try-catch-finally块下面列出的代码将不会运行。


视语言而定,因为语义上可能会有一些细微的差异,但是想法是,即使try块中的代码引发异常,它也将始终(几乎)执行。

在第二个示例中,如果catch块中的代码返回或退出,则x = 3将不会执行。首先,它将。

在.NET平台中,某些情况下将不会执行finally块:
安全异常,线程挂起,计算机关闭:)等。


在Java中:

无论是否在catch()中正确捕获了异常,或者实际上是否有捕获,最终总是被调用。


最后尝试捕获是非常重要的构造。您可以确定,即使引发异常,也会执行finally块中的代码。处理外部资源以释放它们非常重要。垃圾收集不会帮您做到这一点。最后,您不应该具有return语句或引发异常。可以这样做,但这是一种不好的做法,并且可能导致不可预测的结果。

如果您尝试以下示例:

1
2
3
4
5
try {
  return 0;
} finally {
  return 2;
}

结果将是2 :)

与其他语言的比较:从最后返回


有以下几点使finally块有用:

  • 如果从try或catch块返回,则在将控制权交还给调用函数之前,finally块仍将执行
  • 如果catch块内发生异常,或者try块内发生未捕获的异常,则finally块中的代码仍将执行。
  • 这些使块最终成为关闭文件句柄或套接字的绝佳选择。


    在尝试和捕获为空的情况下,没有区别。否则,您可以确定将执行finally。

    例如,如果您在catch块中抛出一个新的Exception(重新抛出),则仅当该赋值位于finally块中时,才会执行该赋值。

    通常,finally用于在您自己之后清理(关闭DB连接,文件句柄等)。

    最后,您永远不要使用控制语句(返回,中断,继续),因为这可能是维护的噩梦,因此被认为是不好的做法


    即使抛出异常或到达return语句(尽管可能与语言相关),也会始终调用finally块(不是很总是...)。这是一种清理方法,您知道它将始终被调用。


    @iAn和@mats:

    通常,我不会"拆除"最终{}中在try {}中"设置"的任何内容。将流创建创建到try {}之外会更好。如果您需要处理流中的异常,则可以在更大的范围内完成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    StreamReader stream = new StreamReader("foo.bar");  
    try {
        mySendSomethingToStream(stream);
    }
    catch(noSomethingToSendException e) {
        //Swallow this    
        logger.error(e.getMessage());
    }
    catch(anotherTypeOfException e) {
        //More serious, throw this one back
        throw(e);
    }
    finally {
        stream.close();  
    }

    最终,代码块使您作为开发人员可以自己整理一下,而不管try {}代码块中的代码前面的操作遇到了什么错误,并且其他人已经指出了这一点,这主要是在释放资源的保护下-关闭指针/套接字/结果集,将连接返回池等

    @mats是非常正确的,它总是存在"硬"故障的可能性-最后,块不应包含关键任务代码,而这些代码始终应在try {}中以事务方式完成

    再次@mats-真正的好处是它允许您将异常抛出自己的方法之外,并且仍然保证您可以整理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    try
    {
    StreamReader stream = new StreamReader("foo.bar");
    mySendSomethingToStream(stream);
    }
    catch(noSomethingToSendException e) {
        //Swallow this    
        logger.error(e.getMessage());
    }
    catch(anotherTypeOfException e) {
        //More serious, throw this one back
        throw(e);
    }
    finally
    {
    stream.close();
    }

    因此,我们可以捕获许多类型的异常,对它们进行不同的处理(第一种允许执行try {}以外的任何事物,第二种有效地返回),但始终要整洁地清除。


    这个问题很老,但这一直困扰着我(我在这里找到它是有原因的)。我已经阅读了所有答案,但在我看来,没有人真正想到它。

    这不是答案。我真的认为finally没有什么意义,这也许就是为什么直到最近才在编程语言中不存在它的原因。大多数说明stream.close()的示例都可能导致空引用异常,因此您仍然必须测试它是否为空。

    是的,如果您从try{}内部返回,则finally仍会运行。但这是好习惯吗?看起来像体操,不妨带回goto。为什么不等待并在阻止之后返回? finally {}所做的只是在代码中添加两行或三行。


    无论您是否捕获到异常,都应该执行finally块。
    请参阅尝试/捕获/最终示例


    因此,您可以清理在try块中初始化的所有打开的连接等。如果您打开了一个连接,然后发生了异常,则该异常将无法正确关闭。这种类型的情况就是finally块的用途。


    finally块与try / catch的作用域相同,因此您可以访问其中定义的所有变量。

    假设您有一个文件处理程序,这就是编写方式的区别。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try
    {
       StreamReader stream = new StreamReader("foo.bar");
       stream.write("foo");
    }
    catch(Exception e) { } // ignore for now
    finally
    {
       stream.close();
    }

    相比

    1
    2
    3
    4
    5
    6
    7
    8
    9
    StreamReader stream = null;
    try
    {
        stream = new StreamReader("foo.bar");
        stream.write("foo");
    } catch(Exception e) {} // ignore

    if (stream != null)
        stream.close();

    请记住,尽管最终里面的任何内容都不能保证运行。想象一下,您收到异常终止信号,窗户崩溃或电源关闭。最终依赖于业务关键代码是不好的。


    在发生未处理的异常的情况下,finally中的所有代码均会运行。通常,finally代码用于使用.dispose()清理非托管代码的本地声明。


    在Java中,无论您使用的是"返回",只是运行try块还是捕获了异常,您都可以将它用于任何要执行的操作。

    例如,关闭数据库会话或JMS连接,或取消分配某些OS资源。

    我猜它在.NET中类似吗?


    @Ed,您可能会想到catch(...)之类的东西,它会捕获C ++中未指定的异常。

    但是finally是无论catch块中发生什么情况都将被执行的代码。

    Microsoft最终会在C#上提供帮助页面


    推荐阅读