关于c#:在try / catch / finally中“finally”的目的是什么

What is the purpose of “finally” in try/catch/finally

语法将从语言变为语言,但这是一个普遍的问题。

这有什么区别....

1
2
3
4
5
6
7
8
9
10
11
12
13
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.");
}

还有这个....

1
2
3
4
5
6
7
8
9
10
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.");

我一直看到它被使用,所以我认为有一个很好的理由最终使用,但我无法弄清楚它是如何只是在语句之后放置代码,因为它仍然会运行。

有没有一个场景最终没有运行?


在你的例子中,它并没有产生很大的不同。

想象一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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
2
3
4
5
6
7
8
9
10
11
12
13
14
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");


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

恕我直言Finally范围强调了这样一个事实,即它的代码包含在异常情况下值得特别注意的东西。


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


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

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

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

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

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


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


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

1
2
3
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及更低版本中,人们可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()方法来替换块:

1
2
3
4
} finally {
    IOUtils.closeQuietly(is);
    IOUtils.closeQuietly(os);
}

但Java 7引入了更好的解决方案:尝试使用资源。 C#4可能首先出现类似的东西,微软的速度比Snoracle更快。

1
2
3
4
5
6
try (
    is = new FileInputStream("foo.txt"),
    os = new FileOutputStream("D:/nonexistent/directory/bar.txt")
) {
    // work...
}


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


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


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