代码在最后语句火,如果我返回一个值在一个尝试块?

我正在为一个朋友检查一些代码,并说他在try-finally块中使用了return语句。即使try块的其余部分没有触发,Finally部分中的代码仍然会触发吗?

例子:

public bool someMethod()
{
try
{
return true;
throw new Exception("test"); // doesn't seem to get executed
}
finally
{
//code in question
}
}
87673 次浏览

简单的回答:是的。

通常情况下,是的。finally部分保证执行发生的任何事情,包括异常或返回语句。该规则的异常是发生在线程(OutOfMemoryExceptionStackOverflowException)上的异步异常。

要了解在这种情况下异步异常和可靠代码的更多信息,请阅读约束执行区域

是的。这实际上是最后一个陈述的主要观点。除非发生了一些灾难性的事情(内存不足,计算机拔插等),finally语句应该始终执行。

引用自MSDN

最后用于保证语句代码块的执行,而不管前面的试一试块是退出

这里有一个小测试:

class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("before");
Console.WriteLine(test());
Console.WriteLine("after");
}


static string test()
{
try
{
return "return";
}
finally
{
Console.WriteLine("finally");
}
}
}

结果是:

before
finally
return
after

通常是的,最终会运行。

对于以下三种情况,finally将运行总是:

  1. 没有异常发生
  2. 同步异常(在正常程序流中发生的异常) 这包括派生自System的符合CLS的异常。异常和非cls兼容的异常,这些异常不是从System.Exception派生的。非cls兼容的异常被RuntimeWrappedException自动包装。c#不能抛出非cls的抱怨异常,但c++等语言可以。c#可以调用用一种可以抛出非cls兼容异常的语言编写的代码 <李> 异步ThreadAbortException < br > 从。net 2.0开始,ThreadAbortException将不再阻止finally的运行。ThreadAbortException现在被提升到finally之前或之后。finally将始终运行,并且不会被线程中止中断,只要在线程中止发生之前实际输入了try

下面的场景,最后将不会运行:

< p > 异步StackOverflowException。 < br > 从。net 2.0开始,堆栈溢出将导致进程终止。finally将不会运行,除非应用进一步的约束使finally成为CER(受限执行区域)。cer不应该在一般用户代码中使用。它们应该只用于始终运行清理代码非常关键的地方——无论如何,在所有进程因堆栈溢出而关闭之后,所有托管对象将因此默认被清理。因此,唯一应该与CER相关的地方是分配在进程外部的资源,例如,非托管句柄

通常,非托管代码在被用户代码使用之前由某个托管类包装。托管包装器类通常会使用SafeHandle来包装非托管句柄。SafeHandle实现了一个关键的终结器和一个在CER中运行的Release方法,以保证清理代码的执行。出于这个原因,您不应该看到cer遍布整个用户代码。

因此,finally不运行在StackOverflowException上的事实应该对用户代码没有影响,因为进程无论如何都会终止。如果你有一些边缘情况,你确实需要清理一些非托管资源,在SafeHandle或CriticalFinalizerObject之外,然后使用CER如下所示;但请注意,这是一种糟糕的做法——非托管概念应该通过设计抽象为托管类和适当的安全句柄。

例如,

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// This is *NOT* a CER
}
finally
{
// This is a CER; guaranteed to run, if the try was entered,
// even if a StackOverflowException occurs.
}

最后不会运行,如果你正在退出应用程序使用 system . exit (0);在< / p >

try
{
System.out.println("try");
System.exit(0);
}
finally
{
System.out.println("finally");
}

结果将是: 试着< / p >

我意识到我迟到了,但在确实抛出异常的场景(与OP的例子不同)中,MSDN在操作系统是否选择触发异常展开操作时声明(https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx): 如果异常没有被捕获,finally块的执行取决于。”

如果调用堆栈上的其他函数(如Main)捕捉到异常,finally块只执行保证。这个细节通常不是问题,因为所有运行时环境(CLR和OS) c#程序在退出时运行在进程拥有的空闲资源上(文件句柄等)。在某些情况下,这可能是至关重要的:一个正在进行的数据库操作,您希望提交响应。放松;或者一些远程连接可能不会被操作系统自动关闭,然后阻塞服务器。

它也不会触发未捕获的异常,并运行在Windows服务托管的线程中

最后不执行时,在一个线程运行在Windows服务

99%的情况下,可以保证finally块中的代码将运行,但是,考虑一下这种情况:你有一个线程,它有一个try->finally块(没有catch),并且你在该线程中得到一个未处理的异常。在这种情况下,线程将退出,它的finally块将不会被执行(在这种情况下,应用程序可以继续运行)。

这种情况非常罕见,但这只是为了表明答案并不总是“是”,大多数情况下是“是”,有时在极少数情况下是“否”。

finally块的主要目的是执行写入其中的内容。它不应该取决于try或catch中发生的任何事情。但是使用System.Environment.Exit(1),应用程序将退出而不移动到下一行代码。

有一个非常重要的例外,我在其他任何答案中都没有看到过,而且(在用c#编程了18年之后)我不敢相信我不知道。

如果你在你的catch块中抛出或触发一个任何排序异常(不仅仅是奇怪的StackOverflowExceptions和类似的东西),并且你没有将整个try/catch/finally块放在另一个try/catch块中,你的finally块将不会执行。这很容易证明-如果我没有自己看到它,鉴于我经常读到它只是非常奇怪的,微小的角落情况,可以导致finally块不执行,我不会相信它。

static void Main(string[] args)
{
Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
try
{
Console.WriteLine("Inside try but before exception.");
throw new Exception("Exception #1");
}
catch (Exception ex)
{
Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
throw;
}
finally
{
Console.WriteLine("This never gets executed, and that seems very, very wrong.");
}


Console.WriteLine("This never gets executed, but I wasn't expecting it to.");
Console.ReadLine();
}

我相信这是有原因的,但奇怪的是,它并没有广为人知。(例如,它被标记为在这里,但在这个特定的问题中没有任何地方。)