如果我在 Try 块中返回一个值,Finally 语句中的代码会触发吗?

Posted

技术标签:

【中文标题】如果我在 Try 块中返回一个值,Finally 语句中的代码会触发吗?【英文标题】:Will code in a Finally statement fire if I return a value in a Try block? 【发布时间】:2010-09-25 14:19:32 【问题描述】:

我正在为一位朋友审查一些代码,并说他在 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
  

【问题讨论】:

***.com/q/449099 被处理这里的意思是:被抓住。即使是全局处理程序中的一个空 catch 块也足够了。此外,还有一些无法处理的异常:***ExceptionExecutionEngineException 就是其中的一些。而且由于它们无法处理,finally 将无法运行。 @Abel:您似乎在谈论另一种情况。这个问题是关于在try 块中返回。与突然的程序中止无关。 @Abel:我不确定您所说的“异常后返回”是什么意思,但这似乎不是这里要问的问题。查看代码——try 块的第一条语句是return 语句。 (该块的第二条语句不可访问,会产生警告。) @Abel:确实,如果问题是“finally 语句中的代码是否总是在每种情况下都执行”,这将是相关的。但这不是被问到的。 【参考方案1】:

简单的回答:是的。

【讨论】:

@Edi:嗯,我不明白后台线程与它有什么关系。基本上,除非整个过程中止,否则finally 块将执行。 长答案是,如果发生灾难性事件,例如堆栈溢出、内存不足异常、某种硬崩溃,或者如果有人在正确的时间拔掉你的机器。但是出于所有意图和目的,除非您做的事情非常非常错误,否则 finally 块将始终触发。 如果 try 之前的代码由于某种原因而失败,我注意到 finally 仍然会被执行。在这种情况下,您可以在 finally 中添加一个条件,该条件满足条件,仅在代码成功执行时才执行 finally【参考方案2】:

通常,是的。 finally 部分保证执行任何发生的事情,包括异常或返回语句。此规则的一个例外是线程上发生的异步异常(OutOfMemoryException***Exception)。

要详细了解异步异常和在这种情况下的可靠代码,请阅读constrained execution regions。

【讨论】:

【参考方案3】:

是的。这实际上是 finally 语句的要点。除非发生灾难性事件(内存不足、计算机拔掉电源等),否则应始终执行 finally 语句。

【讨论】:

我不同意。参照。我的答案。如果从未捕获到该异常,则 try 块中的一个完全正常的异常足以绕过 finally 块。这让 感到意外 ;-)。 @PeterA.Schneider - 这很有趣。当然,其影响并没有太大变化。如果调用堆栈中没有任何东西会捕获异常,那么这应该意味着(如果我理解正确的话)该进程即将终止。所以它类似于拔掉插头的情况。我想从中得出的结论是,您应该始终拥有***或未处理的异常捕获。 没错,我也是这么理解的。我也觉得这很令人惊讶。 True:最常见的资源将自动释放,特别是内存和打开的文件。但是操作系统不知道的资源——例如打开的服务器或数据库连接——可能会在远程端保持打开状态,因为它们从未被正确关闭;套接字一直徘徊,等等。我想拥有一个***异常处理程序是谨慎的,是的。【参考方案4】:

引用自 MSDN

finally 用于保证语句块的执行,而不管前面的 try 块如何退出

【讨论】:

嗯,除了 Mehrdad 的例外情况,如果你在 try 块中调试,finally 也不会被调用,然后停止调试:)。看来,生活中没有什么是可以保证的。 不完全,引用from MSDN:但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。反过来,这取决于您的计算机的设置方式。. @Abel 在这里提出了一个关键点。每个人都可以看到 finally 块是如何不执行的,例如该程序通过任务管理器中止。但是,就目前而言,这个答案是完全错误的:finally 实际上 保证 即使对于完全普通的程序(碰巧抛出了这个异常)也一无所获。【参考方案5】:

这是一个小测试:

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

【讨论】:

@CodeBlend:与方法调用结果的WriteLine实际执行的时间有关。在这种情况下,return 调用设置方法结果,最后写入控制台 - 在方法退出之前。然后 Main 方法中的 WriteLine 从返回调用中吐出文本。【参考方案6】:

通常是的,finally 会运行。

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

    无异常发生 同步异常(正常程序流程中发生的异常)。 这包括派生自 System.Exception 的符合 CLS 的异常和不派生自 System.Exception 的不符合 CLS 的异常。不符合 CLS 的异常由 RuntimeWrappedException 自动包装。 C# 不能抛出非 CLS 投诉异常,但 C++ 等语言可以。 C# 可能会调用以一种语言编写的代码,该语言会引发不符合 CLS 的异常。 异步 ThreadAbortException 从 .NET 2.0 开始,ThreadAbortException 将不再阻止 finally 运行。 ThreadAbortException 现在被提升到 finally 之前或之后。 finally 将始终运行并且不会被线程中止中断,只要在线程中止发生之前实际输入了 try。

以下场景,finally不会运行:

异步 ***Exception。 从 .NET 2.0 开始,堆栈溢出将导致进程终止。 finally 不会运行,除非应用进一步的约束以使 finally 成为 CER(受约束的执行区域)。 CER 不应用于一般用户代码。它们只应在清理代码始终运行至关重要的情况下使用 - 在所有进程无论如何都会因堆栈溢出而关闭并且因此默认情况下将清理所有托管对象之后。因此,与 CER 相关的唯一位置是分配在进程之外的资源,例如非托管句柄。

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

因此,finally 不在 ***Exception 上运行的事实应该对用户代码没有影响,因为进程无论如何都会终止。如果您确实需要在 SafeHandle 或 CriticalFinalizerObject 之外清理某些非托管资源的边缘情况,请按如下方式使用 CER;但请注意,这是一种不好的做法 - 非托管概念应抽象为托管类和适当的 SafeHandle 设计。

例如,

// 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 ***Exception occurs.

【讨论】:

请注意,即使是 CER 也不会在 SOE 的情况下运行。在你写这篇文章的时候,关于 CER 的 MSDN 文档是错误的/不完整的。 SOE 将在内部触发FailFast。我设法抓住这些的唯一方法是customizing the CLR runtime hosting。请注意,您的观点对于其他一些异步异常仍然有效。【参考方案7】:

如果您使用退出应用程序,最终不会运行 System.exit(0);如

try

    System.out.println("try");
    System.exit(0);

finally

   System.out.println("finally");

结果将是: 试试

【讨论】:

我想你用错误的语言回答,问题是关于c#,但这似乎是Java。除此之外,在大多数情况下,System.exit() 暗示了一个糟糕的设计:-)【参考方案8】:

我意识到我迟到了,但在确实引发异常的情况下(与 OP 的示例不同)MSDN 状态 (https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx):“如果未捕获到异常,则执行finally 块取决于操作系统是否选择触发异常展开操作。"

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

【讨论】:

【参考方案9】:

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

Finally is not executed when in a Thread running in a Windows Service

【讨论】:

【参考方案10】:

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

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

如果您在 catch 块内抛出或触发 any 异常(不仅仅是奇怪的 ***Exceptions 和类似的东西),并且您没有整个 @987654324 @ 块在另一个 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();

我确信这是有原因的,但奇怪的是它并没有更广为人知。 (例如,here 被注明,但在这个特定问题的任何地方都没有。)

【讨论】:

好吧,finally 块根本不适合 catch 块。

以上是关于如果我在 Try 块中返回一个值,Finally 语句中的代码会触发吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java - 如果我在 catch 块中返回,finally 块会被执行吗? [复制]

Java中,finally在try语句块中的return前执行还是后执行

Java中,finally在try语句块中的return前执行还是后执行

异常捕获try----catch

try--catch--finally中return返回值执行的顺序

try--catch--finally中return返回值执行的顺序(区别)