.NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中
不知你是否见过 try { } finally { }
代码中,try
块留空,而只往 finally
中写代码的情况呢?这种写法有其特殊的目的。
本文就来说说这种不一样的写法。
你可以点开这个链接查看 Exception
类,在里面你可以看到一段异常处理的代码非常奇怪:
// 代码已经过简化。 internal void RestoreExceptionDispatchInfo(ExceptionDispatchInfo exceptionDispatchInfo) { // 省略代码。 try{} finally { // 省略代码。 } // 省略代码。 }
神奇之处就在于,其 try
块是空的,重要代码都放在 finally
中。那为什么会这么写呢?
在代码注释中的解释为:
We do this inside a
finally
clause to ensureThreadAbort
cannot be injected while we have taken the lock. This is to prevent unrelated exception restorations from getting blocked due to TAE.
翻译过来是:
在 finally
子句中执行此操作以确保在获取锁时无法注入 ThreadAbort
。这是为了防止不相关的异常恢复因 TAE 而被阻止。
也就是说,此方法是为了与 Thread.Abort
对抗,防止 Thread.Abort
中断此处代码的执行。Thread.Abort
的执行交给 CLR 管理,finally
的执行也是交给 CLR 管理。CLR 确保 finally
块执行的时候不会被 Thread.Abort
阻止。
代码在 .NET Core 和 .NET Framework 中的实现完全一样:
// This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the // exception, just before the exception is "rethrown". [SecuritySafeCritical] internal void RestoreExceptionDispatchInfo(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo) { bool fCanProcessException = !(IsImmutableAgileException(this)); // Restore only for non-preallocated exceptions if (fCanProcessException) { // Take a lock to ensure only one thread can restore the details // at a time against this exception object that could have // multiple ExceptionDispatchInfo instances associated with it. // // We do this inside a finally clause to ensure ThreadAbort cannot // be injected while we have taken the lock. This is to prevent // unrelated exception restorations from getting blocked due to TAE. try{} finally { // When restoring back the fields, we again create a copy and set reference to them // in the exception object. This will ensure that when this exception is thrown and these // fields are modified, then EDI's references remain intact. // // Since deep copying can throw on OOM, try to get the copies // outside the lock. object _stackTraceCopy = (exceptionDispatchInfo.BinaryStackTraceArray == null)?null:DeepCopyStackTrace(exceptionDispatchInfo.BinaryStackTraceArray); object _dynamicMethodsCopy = (exceptionDispatchInfo.DynamicMethodArray == null)?null:DeepCopyDynamicMethods(exceptionDispatchInfo.DynamicMethodArray); // Finally, restore the information. // // Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance, // they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock. lock(Exception.s_EDILock) { _watsonBuckets = exceptionDispatchInfo.WatsonBuckets; _ipForWatsonBuckets = exceptionDispatchInfo.IPForWatsonBuckets; _remoteStackTraceString = exceptionDispatchInfo.RemoteStackTrace; SaveStackTracesFromDeepCopy(this, _stackTraceCopy, _dynamicMethodsCopy); } _stackTraceString = null; // Marks the TES state to indicate we have restored foreign exception // dispatch information. Exception.PrepareForForeignExceptionRaise(); } } }
你可以在 这里 查看 .NET Framework 版本,在这里 查看 .NET Core 的版本。
参考资料
- exception.cs - Reference Source
- RestoreExceptionDispatchInfo
- The empty try block mystery - Some Creativity
- c# - Why use try {} finally {} with an empty try block? - Stack Overflow
- corefx/System.Runtime.cs at master · dotnet/corefx
原文地址: https://walterlv.com/post/empty-try-block.html
作者:吕毅
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述