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

时间:2008-12-05 20:50:17

标签: c# .net exception-handling try-catch

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

12 个答案:

答案 0 :(得分:245)

简单回答:是的。

答案 1 :(得分:196)

通常,是的。 finally部分保证执行包括异常或return语句在内的任何操作。此规则的一个例外是线程上发生异步异常(OutOfMemoryExceptionStackOverflowException)。

要了解有关异步异常和可靠代码的更多信息,请阅读constrained execution regions

答案 2 :(得分:143)

这是一个小测试:

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

答案 3 :(得分:35)

从MSDN引用

  

最终用于保证代码语句块的执行,无论前面的尝试块是如何退出

答案 4 :(得分:18)

一般来说,终于会运行。

对于以下三种情况,最终将始终运行:

  1. 不会发生例外
  2. 同步异常(正常程序流程中发生的异常) 这包括从System.Exception和非CLS兼容的异常派生的CLS兼容异常,这些异常不是从System.Exception派生的。 RunSwpedException会自动包装非CLS兼容的异常。 C#不能抛出非CLS投诉异常,但C ++等语言可以。 C#可以调用用可以抛出非CLS兼容异常的语言编写的代码。
  3. 异步ThreadAbortException
    从.NET 2.0开始,ThreadAbortException将不再阻止finally运行。 ThreadAbortException现在被提升到finally之前或之后。最后将始终运行并且不会被线程中止中断,只要在线程中止发生之前实际输入了try。
  4. 以下场景,终于不会运行:

    Asynchronous StackOverflowException。
    从.NET 2.0开始,堆栈溢出将导致进程终止。除非应用进一步约束以使最终成为CER(约束执行区域),否则最终将不会运行。 CER不应用于一般用户代码。它们只应在清理代码始终运行至关重要的地方使用 - 在所有进程关闭堆栈溢出之后,所有托管对象都将默认清理。因此,CER应该与之相关的唯一地方是在流程外部分配的资源,例如,非托管句柄。

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

    因此,最终不会在StackOverflowException上运行的事实应该对用户代码没有影响,因为该进程无论如何都会终止。如果你有一些边缘情况你需要清理一些非托管资源,在SafeHandle或CriticalFinalizerObject之外,那么使用CER如下;但请注意,这是不好的做法 - 非托管概念应该被抽象为托管类和适当的SafeHandle(s)。

    如,

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

答案 5 :(得分:9)

这是一个非常重要的例外,我在其他任何答案中都没有提到过,而且(在用C#编程18年之后),我无法相信我没有这样做。知道。

如果你在catch区块中抛出或触发任何排序的例外(不仅仅是奇怪的StackOverflowExceptions和那种类似的东西),你就不会这样做。 t将整个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();
}

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

答案 6 :(得分:7)

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

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

答案 7 :(得分:3)

是。这实际上是最终陈述的要点。除非发生某些事情(内存不足,计算机未插入等),否则应始终执行finally语句。

答案 8 :(得分:2)

如果你要退出应用程序,最后不会运行 System.exit(0);如在

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

结果只是: 尝试

答案 9 :(得分:2)

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

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

答案 10 :(得分:0)

99%的方案可以保证finally块中的代码能够运行,但是,请考虑这种情况:您的线程有try - > {{阻止(无finally)并在该线程中得到未处理的异常。在这种情况下,线程将退出并且不会执行其catch块(在这种情况下应用程序可以继续运行)

这种情况非常罕见,但只是为了表明答案并非始终如此"是",大部分时间都是"是"有时,在极少数情况下," No"。

答案 11 :(得分:0)

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