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
语句时执行。无论控制转移是正常执行的结果,还是执行break
、continue
、goto
或return
语句的结果,还是将异常传播到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,即 非常罕见。
事实上,对于任何类型的异步异常(如***Exception
、OutOfMemoryException
、ThreadAbortException
),都不能保证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#“最终”块总是执行吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章