30

语法会因语言而异,但这是一个普遍的问题。

这有什么区别......

try
{
     Console.WriteLine("Executing the try statement.");
     throw new NullReferenceException();
}
catch (NullReferenceException e)
{
     Console.WriteLine("{0} Caught exception #1.", e);
}       
finally
{
     Console.WriteLine("Executing finally block.");
}

和这个....

try
{
    Console.WriteLine("Executing the try statement.");
    throw new NullReferenceException();
}
catch (NullReferenceException e)
{
    Console.WriteLine("{0} Caught exception #1.", e);
}        
Console.WriteLine("Executing finally block.");

我一直看到它被使用,所以我认为有充分的理由使用 finally,但我无法弄清楚它与仅在语句之后放置代码有什么不同,因为它仍然会运行。

有没有最终不运行的情况?

4

10 回答 10

38

在您的示例中,它并没有太大的不同。

想象一下,虽然:

    try
    {
        Console.WriteLine("Executing the try statement.");
        throw new NullReferenceException();
    }
    catch (SomeOtherException e)
    {
        Console.WriteLine("{0} Caught exception #1.", e);
    }       
    finally
    {
        Console.WriteLine("Executing finally block.");
    }

    Console.WriteLine("Executing stuff after try/catch/finally.");

在这种情况下,catch不会捕获错误,因此在整个 try/catch/finally 之后的任何内容都将永远无法到达。但是,finally 块仍将运行

于 2013-05-23T02:37:25.983 回答
13
try
{
    throw new Exception("Error!");
}
catch (Exception ex)
{
    throw new Exception(ex, "Rethrowing!");
}
finally
{
    // Will still run even through the catch kicked us out of the procedure
}

Console.WriteLine("Doesn't execute anymore because catch threw exception");
于 2013-05-23T02:37:42.030 回答
7

这真的取决于 - 其他一些答案有很好的理由使用Finally块。但我认为最好的原因是因为您正在处理异常。您在一个Finally块中所做的事情通常涉及清理资源以确保正确继续,无论是否引发异常 - 对我来说,这仍然是异常处理的一部分,至少是“尝试某事”操作的一部分。

恕我直言,Finally范围强调了这样一个事实,即其代码包含在发生异常时需要特别注意的内容。

于 2013-05-23T02:42:33.607 回答
4

finally 块保证被执行。因此,在您的示例中,两种情况的结果看起来相同。但是如果你在你的 catch 块中使用returnor throw,你可以看到有什么不同。

于 2013-05-23T02:37:52.780 回答
1

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

不管抛出什么异常,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:03:32.887 回答
0

finally 为 try 和 catch 运行。它确保它会运行,但不能 100% 保证它会 [一些错误停止执行代码]

于 2013-05-23T02:34:27.790 回答
0

try 块至少需要一个 catch 或一个 finally。在执行完所有 catch 块之后,finally 块将被执行。您可以在其中添加任何您需要的逻辑,这些逻辑应该最终完成。

于 2013-05-23T02:34:44.583 回答
0

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

于 2013-05-23T02:37:13.687 回答
0

我不知道 C#,但finallyJava 语言中的块的目的是确保放弃系统资源,尤其是在垃圾收集不规则的情况下。所有人的原则都是一样的。考虑块

InputStream is = new FileInputStream("foo.txt");
OutputStream os = new FileOutputStream("D:/nonexistent/directory/bar.txt");
// Throws a FileNotFoundException.

变量is创建成功,占用系统资源。进程一次只能使用固定数量的文件句柄。该变量os永远不会被创建,并且会引发异常,并冒泡到调用者。在此过程中,is超出范围,并有资格进行垃圾收集。

然而,垃圾收集永远不能保证会发生,或者它们可能会在未来某个不确定的时间发生。因此,被占用的系统资源is可能永远不会被释放。这可能代价高昂,或者如果发生的次数足够多,可能会使程序崩溃。因此,finally块被放入 Java 中。其他语言也有类似的结构。在 C++ 中,析构函数被确定性地调用。LISPy 语言有unwind-protect,但它们通常打包在with-foo宏中。

在 Java 6 及更低版本中,可以这样做:

try {
    is = new FileInputStream("foo.txt");
    os = new FileOutputStream("D:/nonexistent/directory/bar.txt");
    // work...
} finally {
    if (is != null) {
        try {
            is.close();
        } catch (IOException ignored) {}
    }
    if (os != null) {
        try {
            os.close();
        } catch (IOException ignored) {}
    } 
}

你不能只是打电话is.close(),因为那可能会抛出,然后os永远不会被关闭。你也得检查一下null。Sane 人使用 Jakarta Commons-IO 的IOUtils.closeQuietly()方法来替换块:

} finally {
    IOUtils.closeQuietly(is);
    IOUtils.closeQuietly(os);
}

但是 Java 7 引入了一个更好的解决方案:try-with-resources。C# 4 可能首先出现了类似的东西,微软比 Snoracle 更快地接受。

try (
    is = new FileInputStream("foo.txt"),
    os = new FileOutputStream("D:/nonexistent/directory/bar.txt")
) {
    // work...
}
于 2013-05-23T03:01:15.380 回答
0

最后总是总是运行。finally 就像一个从不漏掉任何东西的捕手。在您提到的示例中,是的,最终没有增加任何价值。但 finally 通常用于处置/释放资源。

于 2013-05-23T04:34:02.987 回答