不知你是否见过 try { } finally { } 代码中,try 块留空,而只往 finally 中写代码的情况呢?这种写法有其特殊的目的。

本文就来说说这种不一样的写法。


你可以点开这个链接查看 Exception 类,在里面你可以看到一段异常处理的代码非常奇怪:

// 代码已经过简化。
internal void RestoreExceptionDispatchInfo(ExceptionDispatchInfo exceptionDispatchInfo)
{
// 省略代码。
try{}
finally
{
// 省略代码。
}
// 省略代码。
}

神奇之处就在于,其 try 块是空的,重要代码都放在 finally 中。那为什么会这么写呢?

在代码注释中的解释为:

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.

翻译过来是:

在 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 的版本。


参考资料

原文地址: https://walterlv.com/post/empty-try-block.html

作者:吕毅

.NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中的更多相关文章

  1. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  2. (3)润写一个程序(类),用户输入一段英文,然后输出这段英文中所有长度为3个字母的单词井且如果单词如果有连续 复了2次,只输出一个【例: This isis a desk,程序输出 his is a desk】,(提示,有re正则匹配来做)

    import re x = input('Please input a string:') pattern = re.compile(r'\b[a-zA-Z]{3}\b') print(pattern ...

  3. 自己写一个 jQuery 插件

    我知道这一天终将会到来,现在,它来了. 需求 开发 SharePoint 的 CSOM 应用时,经常需要在网页上输出一些信息. 这种需求和 alert 的弹窗.F12 的断点查看信息的场景是不一样的: ...

  4. 学了C语言,如何利用cURL写一个程序验证某个网址的有效性?

    在<C程序设计伴侣>以及这几篇关于cURL的文章中,我们介绍了如何利用cURL写一个下载程序,从网络下载文件.可是当我们在用这个程序下载文件时,又遇到了新问题:如果这个网址是无效的,那么我 ...

  5. 我为什么要再给lua写一个json模块

    最近要给自己编写的服务器加上json解析模块.根据我当前的项目,可以预测服务器中使用json的地方: 通信.由于与客户端通信使用google protocolbuffer,仅在与SDK通信中使用jso ...

  6. 用C写一个web服务器(二) I/O多路复用之epoll

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  7. 用weexplus从0到1写一个app

    说明 基于wexplus开发app是来新公司才接触的,之前只是用过weex体验过写demo,当时就被用vue技术栈来开发app的开发体验惊艳到了,这个开发体验比react native要好很多,对于我 ...

  8. .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?

    .NET 中的 async / await 写异步代码用起来真的很爽,就像写同步一样.我们可以在各种各样的异步代码中看到 Task 返回值,这样大家便可以使用 await 等待这个方法.不过,有时需要 ...

  9. django写一个简单的登陆注册

    要写这个,前提还是需要知道三个知识: 一个是urls.py,它是写我们的路由关系的,之前我写了通过wsgiref写一个简单的服务端,也用到了路由,就是 请求过来的url和视图函数的对应关系. 二是就是 ...

随机推荐

  1. AS插件-GsonFormat

    支持 field 类型的修改. 支持快捷键打开 GsonFormat ,默认为 option+s(mac), alt+s(win) 支持 field 名称的修改. 支持添加 field 前缀. 支持多 ...

  2. JS 调试中常见的报错的解决办法

    报错:Uncaught SyntaxError: Unexpected token o in JSON at position 1 at JSON.parse (<anonymous>) ...

  3. Python 实例方法、类方法、静态方法的区别与作用

    Python中至少有三种比较常见的方法类型,即实例方法,类方法.静态方法.它们是如何定义的呢?如何调用的呢?它们又有何区别和作用呢?且看下文. 首先,这三种方法都定义在类中.下面我先简单说一下怎么定义 ...

  4. [20190320]测试相同语句遇到导致cursor pin S的情况.txt

    [20190320]测试相同语句遇到导致cursor pin S的情况.txt --//前面测试链接:http://blog.itpub.net/267265/viewspace-2636342/-- ...

  5. [C#]关于路径的几则笔记

    测试环境 本文基于windows下的操作 windows 7 x64 unity 5.3.7p4 获取文件的路径 比如一个文件的路径为:e:\3d\client\trunk\Product\Apps\ ...

  6. python设计模式之单例模式(转)

    设计模式之单例模式 单例设计模式是怎么来的?在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法 ...

  7. rabbitMQ模式

    1.hello  消息生产者p将消息放入队列 消费者监听队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列删除(隐患,消息可能没有被消费者正确处理,已经消失了,无法恢复) 应用场景:聊天室 案 ...

  8. python中的猴子补丁Monkey Patch

    python中的猴子补丁Monkey Patch 什么是猴子补丁 the term monkey patch only refers to dynamic modifications of a cla ...

  9. jQuery -- 光阴似箭(四):jQuery 遍历

    jQuery -- 知识点回顾篇(四):jQuery 遍历 通过 jQuery 遍历,您能够从被选(当前的)元素开始,轻松地在家族树中向上移动(祖先),向下移动(子孙),水平移动(同胞).这种移动被称 ...

  10. Linux下编译安装redis

    redis是开源,BSD许可,高级的key-value存储系统,可以用来存储字符串,哈希结构,链表,集合,因此,常用来提供数据结构服务. redis和memcached相比的独特之处: 1: redi ...