在try / catch / finally中“finally”的目的是什么

时间:2013-05-23 02:32:12

标签: c# javascript exception-handling finally

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

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

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.");

我一直看到它被使用了,所以我认为最终使用它是一个很好的理由,但是我无法弄清楚它只是在声明之后放置代码与它仍然会运行有什么不同。

是否存在最终无法运行的情况?

10 个答案:

答案 0 :(得分:28)

在你的例子中,它没有太大的区别。

想象一下:

    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块仍将运行

答案 1 :(得分:9)

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");

答案 2 :(得分:6)

这实际上取决于 - 其他一些答案有充分理由使用Finally块。但我认为最好的原因是因为你正在进行异常处理。您在Finally块中执行的操作通常涉及清理资源以确保正常继续,无论是否抛出异常 - 对我来说仍然是异常处理的一部分,至少“尝试某事”行动的一部分。

恕我直言,Finally范围突出显示其代码包含在异常情况下值得特别关注的内容。

答案 3 :(得分:3)

最后确保块被执行。 因此,在您的示例中,两种情况的结果看起来都相同。 但是如果你在catch块中使用returnthrow,你可以看到有什么区别。

答案 4 :(得分:1)

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

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

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

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

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

答案 5 :(得分:0)

最后运行try和catch。它确保它将运行,但它不是100%保证它会[某些错误停止执行代码]

答案 6 :(得分:0)

尝试块需要至少一个catch或者finally.after执行所有catch块后,finally块将被执行。你可以添加你需要的任何逻辑,最终应该完成。

答案 7 :(得分:0)

最终使用它来处理程序崩溃是一个很好的做法。最终将永远运行。如果函数退出try catch块内部,或者在try或catch中抛出另一个错误,则finally仍将执行。您不会使用finally语句获得该功能。

答案 8 :(得分:0)

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

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...
}

答案 9 :(得分:0)

终于总是运行。终于就像捕手一样从不错过任何东西。在你提到的例子中,是的,最后不会添加任何值。但最后通常用于处置/释放资源。