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

Jam*_*ers 228 .net c# 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
  }
}
Run Code Online (Sandbox Code Playgroud)

And*_*ngs 257

简单回答:是的.

  • @Edi:嗯,我没看到后台线程与它有什么关系.基本上,除非整个过程中止,否则`finally`块将执行. (8认同)
  • 长的答案是,如果发生了灾难性的事情,例如堆栈溢出、内存不足异常、某种严重崩溃,或者有人在正确的时间拔掉了你的机器,你不一定会得到一个 finally 块来运行. 但是出于所有意图和目的,除非您做的事情非常非常错误,否则 finally 块将始终触发。 (5认同)

Meh*_*ari 198

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

要了解有关异常异常和可靠代码的更多信息,请阅读有关受约束的执行区域.


Jon*_*n B 151

这是一个小测试:

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

before
finally
return
after
Run Code Online (Sandbox Code Playgroud)

  • @CodeBlend:它与实际执行方法调用结果的`WriteLine`时有关.在这种情况下,`return`调用设置方法结果,最后写入控制台 - 在方法退出之前.然后Main方法中的`WriteLine`从返回调用中吐出文本. (8认同)

Per*_*der 36

引自MSDN

最后用于保证语句代码块的执行,而不管前面的try块是如何退出的.

  • 好吧,除了Mehrdad的特殊情况之外,如果你在try块中调试,最后也不会被调用,然后停止调试:).看来,生活中没有任何东西得到保证. (2认同)
  • @Abel 在这里提出了一个关键点。每个人都可以看到如果程序通过任务管理器中止,finally 块是如何不被执行的。但就目前的情况来看,这个答案是完全错误的:事实上,即使对于完全普通的程序(碰巧抛出了这个异常),“最终”也无法*保证*任何东西。 (2认同)

小智 19

一般是的,终于会运行.

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

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

以下场景,终于不会运行:

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

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

因此,最终不在StackOverflowException上运行的事实应该对用户代码没有影响,因为该进程无论如何都将终止.如果你有一些边缘情况你需要清理一些非托管资源,在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 StackOverflowException occurs.
}
Run Code Online (Sandbox Code Playgroud)


Ken*_*ith 10

有一个非常重要的例外,我在其他任何答案中都没有提到过,而且(在用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();
}
Run Code Online (Sandbox Code Playgroud)

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


Pet*_*ica 7

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

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