C#“最终”块总是执行吗? [复制]

Posted

技术标签:

【中文标题】C#“最终”块总是执行吗? [复制]【英文标题】:Does the C# "finally" block ALWAYS execute? [duplicate] 【发布时间】: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”。 【参考方案1】:

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

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

Source

有finally块不会执行的情况:

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

【讨论】:

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

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

两种可能:

***Exception ExecutingEngineException

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

事实上,对于任何类型的异步异常(如***ExceptionOutOfMemoryExceptionThreadAbortException),都不能保证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 部分时执行。

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

【讨论】:

这还包括一些异常(三个 .net 异常不能被捕获)、Application.FailFast 或断电 (thedailywtf.com/Articles/My-Tales.aspx) @DOK Return 语句有什么问题? "...只要应用程序仍在运行。"很棒的收获。 It's good to keep this in mind - finally is not absolutely guaranteed. @Remus:不,finally 将在无限循环完成后立即被调用...【参考方案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】:

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

【讨论】:

不,它没有。 thedailywtf.com/Articles/My-Tales.aspx @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 块。很高兴知道它没有。 :)

以上是关于C#“最终”块总是执行吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

关于C#中的timer控件!

python Jupyter笔记本 - 常见的东西我最终总是复制粘贴

C# graphics图像复制时提示内存不足

C# 中结构体的复制

C# 中结构体的复制

如何在 Unity C# 中将 OnPointerDown 侦听器添加到滑块句柄? [复制]