【问题标题】:Does the C# "finally" block ALWAYS execute? [duplicate]C#“最终”块总是执行吗? [复制]
【发布时间】:2011-03-14 01:30:42
【问题描述】:

可能重复:
Will code in a Finally statement fire if I return a value in a Try block?

考虑以下代码 C# 代码。 “finally”块是否执行?

public void DoesThisExecute() {
   string ext = "xlsx";
   string message = string.Empty;
   try {
      switch (ext) {
         case "xls": message = "Great choice!"; break;
         case "csv": message = "Better choice!"; break;
         case "exe": message = "Do not try to break me!"; break;
         default:
            message = "You will not win!";
            return;
      }
   }
   catch (Exception) {
      // Handle an exception.
   }
   finally {
      MessageBox.Show(message);
   }
}

哈,写完这篇文章后,我意识到我自己可以在 Visual Studio 中完成测试。但是,请随时回答!

【问题讨论】:

  • 没有。你会得到一个编译器错误。
  • 唯一不能编译的是声明'ext'后缺少的分号。
  • @Zano - 失踪的;是一个错字:)
  • 如果程序出乎意料地崩溃(灾难性的运行时错误就是这样一种异常,通常是通过调用 P/Invoke 的代码而造成的,这会弄乱堆栈),或者机器断电,那么不, finally-block 不会执行。
  • @Charles 为什么要这样? catch(Exception) 非常好,不需要指定一个变量来“catch into”。

标签: c# try-catch


【解决方案1】:

来自 try 语句的 MSDN C# 规范:

finally 块的语句总是在控制离开try 语句时执行。无论控制转移是正常执行的结果,还是执行 breakcontinuegotoreturn 语句的结果,还是将异常传播到try 声明。

Source

有finally块不会执行的情况:

  1. Environment.FailFast
  2. 无法捕获的异常类型
  3. 电源故障

【讨论】:

  • @Gary Willoughby,我知道确实如此,但我不想错,所以我只是在谷歌上搜索了规范。
  • 规格有误。试试这个: static int Main( string[] arguments ) { try { return arguments[1000].Length; } finally { System.Console.WriteLine("你永远不会看到这个!"); } }
  • 哇,谈论一个好问题的错误答案......这不是所指出的那个的重复,因为这个问题与执行时的 finally 块有关 return 语句在 try 块中,在这种情况下,答案“是”。根据这个问题的标题,在更一般的情况下,至少有三种不同的情况,finally 块将被执行(甚至不包括外部影响,例如系统崩溃、进程被杀死、等等。)。看到这个问题:StackOverflow.com/questions/19549613/…
  • @Mike Nakis:我刚试过你的 sn-p。如果你运行它,“我的程序已停止工作”窗口出现,如果你点击取消,它会显示该行。 (为发布而构建)
  • 我认为 finally 块中的代码会向 CPU 发出信号,指示 PSU 将存储在备用电容器中的最后一位电子直接路由到运行 finally 代码的核心,以防万一电源故障(当然是 CLR 感知电源)。
【解决方案2】:

finally 总是被执行并不完全正确。见this answer 来自Haacked

两种可能:

  • StackOverflowException
  • ExecutingEngineException

finally 块不会被执行 当有 StackOverflowException 因为堆栈上没有空间 甚至执行更多代码。它会 当有一个时也不会被调用 ExecutingEngineException,即 非常罕见。

事实上,对于任何类型的异步异常(如StackOverflowExceptionOutOfMemoryExceptionThreadAbortException),都不能保证finally 块的执行。

但是,这些异常是您通常无法从中恢复的异常,并且在大多数情况下,您的进程无论如何都会退出。

事实上,还有至少另一种情况,finally 没有像现在Brian Rasmussen 中描述的那样执行deleted question

我知道的另一种情况是,如果 终结器抛出异常。在那里面 如果进程终止 立即也是如此,因此 保证不适用。

下面的代码说明了问题

static void Main(string[] args) {
   try {
      DisposableType d = new DisposableType();
      d.Dispose();
      d = null;
      GC.Collect();
      GC.WaitForPendingFinalizers();
   } catch {
      Console.WriteLine("catch");
   } finally {
      Console.WriteLine("finally");
   }
}

public class DisposableType : IDisposable {
   public void Dispose() {
   }

   ~DisposableType() {
      throw new NotImplementedException();
   }
}

可靠的 try/catch/finally 必须使用 Constrained Execution Regions (CER)example 由 MSDN 提供:

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
    public IntPtr m_outputHandle;
}

sealed class MySafeHandle : SafeHandle
{
    // Called by P/Invoke when returning SafeHandles
    public MySafeHandle()
        : base(IntPtr.Zero, true)
    {
    }

    public MySafeHandle AllocateHandle()
    {
        // Allocate SafeHandle first to avoid failure later.
        MySafeHandle sh = new MySafeHandle();

        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally
        {
            MyStruct myStruct = new MyStruct();
            NativeAllocateHandle(ref myStruct);
            sh.SetHandle(myStruct.m_outputHandle);
        }

        return sh;
    }
}

以下文章是一个很好的信息来源:

Reliability Best Practices

【讨论】:

  • 能否请您备份您所说的关于ThreadAbortException 未触发finally 的内容?
  • 同意,我很确定 ThreadAbortException 确实会尝试执行 finally 块。它有利于 finally 块而不是实际中止线程,因此后者的保证不如前者。
  • @Steven Sudit,@David:对不起,我可能对此不正确。我不知何故有这个想法,但 MSDN 明确指出finally 将被执行。
  • ThreadAbortException 是一种非常不寻常的情况,因为它可以被捕获,但会自动重新抛出,除非代码具有调用抑制重新抛出的方法所需的权限。由于有整套机制来处理它被抓住,因此它无法绕过finally
  • @CurlyBrace:您看到的是 .NET 4 中引入的更改。尽管调用了 GC.WaitForPendingFinalizers(),但终结器仅在程序结束时运行。
【解决方案3】:

是的,在正常情况下(正如许多其他人指出的那样)。

finally 块对 清理分配的任何资源 try 块以及运行任何 即使存在也必须执行的代码 是一个例外。控制总是 无论如何都传递给finally块 try 块是如何退出的。

而 catch 用于处理 语句中出现的异常 块,finally 用于保证一个 语句块代码执行 不管前面怎么尝试 块已退出。

http://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx

【讨论】:

    【解决方案4】:

    不,它没有。如果应用程序仍在运行,它将始终执行(FastFail 异常期间除外,MSDN link,如其他人所述)。它会在退出块的 try/catch 部分时执行。

    如果应用程序崩溃,它将不会执行:通过终止进程命令等被终止。这非常重要,因为如果您编写的代码绝对期望它运行,例如手动执行回滚,否则不明智它会自动提交,您可以在此之前遇到应用程序中止的场景。老实说,这是一种外部情况,但在这些情况下要注意这一点。

    【讨论】:

    【解决方案5】:

    finally 块将运行,就在这几行之间:

    message = "You will not win!";
    return;
    

    【讨论】:

      【解决方案6】:

      没有。

      但只有一种方法可以解决它,那就是Environment.FailFast()。见http://msdn.microsoft.com/de-de/library/ms131100.aspx。在其他所有情况下,都保证终结器被执行;-)

      FailFast 方法写入消息 到 Windows 应用程序的字符串 事件日志,创建您的转储 应用程序,然后终止 当前进程。消息字符串是 也包括在错误报告中 微软。

      使用 FailFast 方法而不是 终止您的退出方法 应用程序,如果你的状态 应用程序损坏无法修复, 并执行您的应用程序 try/finally 块和终结器将 损坏的程序资源。

      【讨论】:

      • 任何直接终止进程而不是让它自行关闭的行为都会阻止finally 运行。
      【解决方案7】:

      正确答案是肯定的。

      尝试调试您的程序并设置一个断点并观察控制仍然到达 finally 块。

      【讨论】:

        【解决方案8】:

        来自 MSDN try-finally (C# Reference)

        finally 块对 清理分配的任何资源 try 块以及运行任何 即使存在也必须执行的代码 是一个例外。 控制始终如一 无论如何都传递给finally块 try 块是如何退出的

        【讨论】:

          【解决方案9】:

          是的,finally 总是执行,现在 finally 块中的代码是否会导致异常是另一回事。

          【讨论】:

            【解决方案10】:

            【讨论】:

            • @IvanZlatanov 在问题的上下文中答案是正确的,在该问题中,OP 显然想知道 finally 是否会在 return 语句之后执行,但是你是对的,在某些情况下 finally不会执行。
            • 哦,你会感到惊讶。看看这个: static int Main( string[] arguments ) { try { return arguments[1000].Length; } finally { System.Console.WriteLine("你永远不会看到这个!"); } }
            • @MikeNakis 我试过了,最后显示了消息。我正在使用 .NET 4.5.1
            • 如果你放置“Process.GetCurrentProcess().Kill();”或 "Environment.FailFast("failfast");"在 try 块中,finally 块不会执行。并且一些未释放的资源或未回滚的事务可能会导致问题。
            【解决方案11】:

            简单的回答是的。但该规则有一些“例外”。

            【讨论】:

            • 你看到return声明了吗?
            • @Gary:退货声明不是借口。
            • 不管return语句如何,还是会进入Finally语句。
            • 我很想知道有哪些例外情况。
            • 谢谢!我不知道,我的印象是return 语句绕过了finally 块。很高兴知道它没有。 :)
            猜你喜欢
            • 2011-01-25
            • 2018-08-22
            • 1970-01-01
            • 1970-01-01
            • 2019-07-29
            • 2010-10-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多