【问题标题】:What is the purpose of "finally" in try/catch/finallytry/catch/finally 中“finally”的目的是什么
【发布时间】:2013-05-18 06:28:46
【问题描述】:

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

这有什么区别....

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 是有充分理由的,但我无法弄清楚它与仅在语句之后放置代码有何不同,因为它仍然会运行。

有没有 finally 不运行的场景?

【问题讨论】:

标签: c# javascript exception-handling finally


【解决方案1】:

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

不过,请想象一下:

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

【讨论】:

  • 啊,这很有意义。谢谢。
  • 有趣的是,我从未见过它与 throw 一起使用。我想这只是一个好习惯。
  • @Maxx:更多的是处理您最终遇到未考虑到的异常的情况。无论您如何退出 try/catch,finally 最终都会运行,即使这是由于您不知道可能发生的错误。所以这是一个放置清理代码等的更好的地方。
  • 或者没有catch块的时候。然后,如果 try 块中的任何代码抛出错误,异常将向上传播到调用例程,在堆栈的某个位置进行处理,或者作为未处理的异常处理。但是 finally 块中的代码仍然会运行。
  • 或者当你在一些纯错误清理后重新抛出异常时。或者基本上任何其他时间您可能会通过异常离开该块。在这些情况下,块之后的代码将不会运行。这就是finally 的用武之地。
【解决方案2】:
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");

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 我就是这么想的,如果你return in catch,finally 还是会执行的,对吧?
      【解决方案5】:

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

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

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

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

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 但是把它放在 try-catch 块之外不是这样吗?
        • 很多时候catch会重新抛出错误或停止执行,这种情况下finally中的代码仍然会运行,但是try catch块之后的代码不会。
        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            我不懂 C#,但 Java 语言中 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。理智的人使用 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...
            }
            

            【讨论】:

            • C# 有using,它与IDisposable(.net 中的接口,表示持有除 RAM 以外的本机资源的类)的实现大致相同。 using (var r = new SomeNativeResource()) { /* do stuff with r; */ } 几乎可以转换为 var r = new SomeNativeResource(); try { /* do stuff with r; */ } finally { if (r != null) r.Dispose(); }。自从... 1.1 或更早版本就已经存在了?...MSDN 至少有一个针对 VS 2003 的页面。
            • 原始帖子被声明为通用的。我想你需要 C# 中的 finally 块,如果你必须做一些你不能翻译成IDisposable 的事情。这可能很少见。
            • 是的...finally C# 中的块并不像 Java 中那么常见。 using 涵盖了 80% 的用例。
            【解决方案10】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-11-27
              • 2011-10-31
              • 1970-01-01
              • 2010-11-12
              • 1970-01-01
              • 2015-09-05
              相关资源
              最近更新 更多