当我们提到CLR里的“异常”,要注意一个很重要的区别。有通过如C#的try/catch/finally暴露给应用程序,并由运行时提供机制全权实现的托管异常。也有运行时自己使用的异常。大部分运行时开发人员很少需要想到如何实现并暴露托管异常模型。但每个运行时开发人员都应该懂得CLR实现里是怎么使用异常的。为了保持区分,本文将托管程序抛出并捕捉的称为托管异常,而将运行时自己使用的错误处理方式称为 CLR内部异常。本文主要讨论CLR内部异常。

异常在什么地方有用?

异常几乎在所有地方都有用。最有用的地方就是抛出或捕捉异常的函数里,因为需要显式编写代码来抛出异常或者捕捉其并优雅的处理异常。即使一个函数本身不抛出异常,它也有可能调用抛出异常的函数。这样该函数必须在异常抛出的时候行为正常。明智的使用支持物(holders)可以极大简化正确编写这类代码。

为什么CLR内部异常是不同的?

CLR内部异常更像C++异常,但不完全是。CLR可以在Mac OSX、BSD还有Windows下编译。操作系统和编译器的差异使得我们不能仅使用标准C++的try/catch。另外,CLR内部异常还提供了类似托管代码的“finally”和“fault”这样的功能。

通过一些宏,编写异常处理代码就像标准C++那样简单。

捕捉异常

EX_TRY

最基本的宏是:EX_TRY / EX_CATCH / EX_END_CATCH,使用方法如下:

  1. EX_TRY
  2. // 调用一些函数,也许会抛出一个异常
  3. Bar();
  4. EX_CATCH
  5. // 在这里,那就有错误发生了
  6. m_finalDisposition = terminallyHopeless;
  7. EX_END_CATCH(RethrowTransientExceptions)

EX_TRY宏就是引入try块,很像C++的“try”,除了其还添加了一个大括号:“{”。

EX_CATCH

EX_CATCH宏结束一个try块,并添加一个大括号:“}”,并且开始catch块。跟EX_TRY类似,其也添加了一个大括号来开始catch块。

这里和C++异常有很大的不同:CLR开发者根本不明确捕捉什么。实际上,这些宏捕捉包括类似AV的非C++异常或托管异常的任何东西。如果一块代码只需要捕捉一个或者一小部分异常,那么它需要捕捉并检查异常,然后将所有不相关的异常再次抛出。

需要再次指明的是EX_CATCH宏捕捉任何东西。这个可能不是一个函数需要的。下两个章节讨论如何处理不应该被捕捉的异常。

GET_EXCEPTION() & GET_THROWABLE()

当一个CLR开发人员捕捉到一个东西,那么他要如何决定做什么?取决于需求,有几个选项:

第一,无论捕捉到什么(C++)异常,都是继承自全局的Exception类的类的实例。一些继承类很明显,如OutOfMemoryException。另一些则有些领域相关,如EETypeLoadException。还有些类只是系统异常的简单封装,如CLRException(包含OBJECTHANDLE字段指向一个托管异常),或HRException(HRESULT的封装)。如果最初的异常不是从Exception继承来的,那么宏会给其做一个封装。(注意所有异常都是系统自带而且众所周知的)。

第二,每个CLR内部异常都有一个关联的HRESULT值。有时像HRException那样,值从某个COM对象来的,但内部异常和Win32 api错误值也有HRESULT值。

最后,几乎所有CLR内部发生的异常都有可能传递到托管代码那边,CLR内部异常都有跟其对应的托管异常。创建托管异常不是必须的,但是总有办法获取它。

那么,CLR开发人员将如何给一个异常分类呢?

常用的做法是,通过异常关联的HRESULT值分类,而且有一个很简单的办法取值:

  1. HRESULT hr = GET_EXCEPTION()->GetHR();

通过对应的托管异常对象获取更多信息是更便捷的办法。如果异常要传递到托管代码,无论是即时还是被捕捉稍后处理,都是需要这个托管对象的。而且这个异常对象也很容易读取,其是一个托管的objectref引用,因此可以用常规办法:

  1. OBJECTREF throwable = NULL;
  2. GCPROTECT_BEGIN(throwable);
  3. // . . .
  4. EX_TRY
  5. // . . . do something that might throw
  6. EX_CATCH
  7. throwable = GET_THROWABLE();
  8. EX_END_CATCH(RethrowTransientExceptions)
  9. // . . . do something with throwable
  10. GCPROTECT_END()

有时,虽然是异常实现的底层,无法避免要用到C++异常对象。如果C++异常的类型很重要,也有一些轻量级的RTTI函数来帮助归类异常,如:

  1. Exception *pEx = GET_EXCEPTION();
  2. if (pEx->IsType(CLRException::GetType())) {/* ... */}

可以反馈一个异常是否是(或继承自)CLRException。

EX_END_CATCH(RethrowTransientExceptions)

在上面的例子中,“RethrowTransientExceptions”是宏EX_END_CATCH的一个参数;它是三个预定义的宏,并可以看成“异常的性格”。下面是这些宏的解释:

  • SwallowAllExceptions: 命名很简单巧妙。如名字所示,它吞没任何对象。显而易见,通常不是正确的做法。
  • RethrowTerminalExceptions: 一个更好的名字应该是"RethrowThreadAbort", 也就是这个宏的作用。
  • RethrowTransientExceptions:"临时"异常的最好定义是,如果重试则该异常在其它环境里有可能不再发生。下面这些是临时异常:
    • COR_E_THREADABORTED
    • COR_E_THREADINTERRUPTED
    • COR_E_THREADSTOP
    • COR_E_APPDOMAINUNLOADED
    • E_OUTOFMEMORY
    • HRESULT_FROM_WIN32(ERROR_COMMITMENT_LIMIT)
    • HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)
    • (HRESULT)STATUS_NO_MEMORY
    • COR_E_STACKOVERFLOW
    • MSEE_E_ASSEMBLYLOADINPROGRESS

CLR开发人员在不确定的情况下一般应该使用RethrowTransientExceptions.

但在任何情况下,编写EX_END_CATCH的开发人员都需要考虑捕捉哪些异常,并只捕捉这些异常。而且,因为这个宏捕捉所有的东西,不去捕捉一个异常的唯一方法就是重新抛出它。

如果一个EX_CATCH / EX_END_CATCH块正确分类异常,并在必要的时候重新抛出,那么SwallowAllExceptions就是告诉宏不必重新抛出异常的办法。

EX_CATCH_HRESULT

有的时候需要的就是异常对应的那个HRESULT值,特别是针对COM的代码。对于这些情况,使用EX_CATCH_HRESULT宏比编写一个EX_CATCH块简单的多。一个典型代码片段如下:

  1. HRESULT hr;
  2. EX_TRY
  3. // code
  4. EX_CATCH_HRESULT (hr)
  5.  
  6. return hr;

然而,虽然很诱人,但不总是正确的。EX_CATCH_HRESULT捕捉所有的异常,保存HRESULT,并丢掉原始异常。因此,除非丢掉异常这个行为是函数所需要的,否则EX_CATCH_HRESULT并不是很合适。

EX_RETHROW

如上所述,异常宏捕捉所有异常;捕捉一个指定异常的唯一办法是先捕捉所有的异常,再将除了要捕捉的其它异常再次抛出。因此,当一个异常被捕捉,处理之后,结果其不是要被捕捉的,那它可能会被重新抛出。EX_RETHROW宏就是用来抛出相同异常的。

作者:懿民
链接:https://www.jianshu.com/p/9b3d348c292d
来源:简书。

CLR内部异常(上)的更多相关文章

  1. CLR内部异常(下)

    直接使用SEH 有些情况里直接使用SEH会更合适一些.特别是,如果需要在第一次遍历(first pass - SEH异常处理流程里的第一遍处理)时需要执行某些操作时,也就是在堆栈向上展开之前,SEH是 ...

  2. CLR内部异常(中)

    不捕捉某一个异常 常常有这种情况,代码不需要捕捉异常,但需要执行一些清理或者修正操作.虽然不总是,支持物(holders)经常用在这种场景里.在支持物(holders)不适用的情况里,CLR提供了两个 ...

  3. EF并发问题,在提供程序连接上启动事务时出错。有关详细信息,请参阅内部异常。

    1 Entities data=new Entities(); var list = from p in data.Record where p.CreateTime >= d &&am ...

  4. 【性能诊断】六、并发场景的性能分析(windbg案例,大量的内部异常造成CPU飙升)

    在做产品的某个核心模块的性能优化时,发现压到100并发时应用服务器的CPU就飙升至90%以上,50并发以后TPS就基本定格在一个数值上.使用性能监视器收集应用服务器的数据,发现每秒的.NET CLR ...

  5. 查看.NET应用程序中的异常(上)

    内存转储是查明托管.NET应用程序中异常的原因的一种极好的方法,特别是在生产应用程序中发生异常时.当您在无法使用Visual Studio的应用程序中跟踪异常时,cdb和sos.dll的使用技术就变成 ...

  6. ORACL内部异常:

    ORACL内部异常: ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 OR ...

  7. TP5内部异常API数据输出的自定义方法编写

    需求:利用postman进行请求api接口过程中 关于一些数据输出异常的情况下 我们希望通过自己编写一些类和方法 实现便于后端人员进行根据提示进行调试处理! 以下测试的时候 请设置 app_debug ...

  8. 聊聊“装箱”在CLR内部的实现

    原文连接:https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/ 作者 Matt Warren. ...

  9. Dynamics AX 2012 R2 AIF 内部异常

        今天,Reinhard发现某个入站端口,突然一直报错: The server was unable to process the request due to an internal erro ...

随机推荐

  1. Android Studio中设置与Eclipse中 Ctrl+1 功能类似的快捷键

    Eclipse:Ctrl + 1 Android Studio: Alt + Enter 1. 首先当然是打开设置窗口啦,然后在IDE Settings 下找到Keymap 2. 在Keymap的搜索 ...

  2. linux上文件的上传和下载

    现整理一篇linux上文件的上传和下载 第一种方式就是在windos上安装工具 如: 工具如何使用我就不赘述了,easy 第二种方式就是使用liux的命令(首先是文件上传) 上传文件(首先创建文件夹如 ...

  3. Drool7s kmodule的作用--系列02课

    本文是介绍drool7s kmodule. 一.为什么komdule.xml文件一定要放在resources下的META-INF文件夹中 --->直接看源码吧,请看下图,应该都知道为什么要放在固 ...

  4. C#静态字段的两个用处

    静态字段的2个常用方法 (1)记录已实例化的对象的个数 (2)存储必须在所有实例化之间共享的值 (1)记录已实例化的对象的个数 现在某个培训机构啊,要开设一个学理发的班,计划招5人,只要人数够5人就开 ...

  5. python 函数的链式调用(一个函数调用使用两个括号)

    # python 函数的链式调用 def funcA(a): def funcB(b): for a_each in a: x = funcB(a_each) return x return func ...

  6. angularJs指令的Scope(作用域)

    每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),还是创建一个新的自己的作用域,当然Angular ...

  7. 软工作业 wc-java

    项目要求: 实现一个统计程序,它能正确统计程序文件中的字符数.单词数.行数,以及还具备其他扩展功能,并能够快速地处理多个文件. 具体功能 -c 返回文件字符数 -w 返回词的数目 -l 返回行数 扩展 ...

  8. 爬取网易云音乐歌手和id

    pip install lxml csv requests from lxml import etree from time import sleep import csv import reques ...

  9. 【BBED】编译及基本命令(1)

    [BBED]编译及基本命令(1) 一.1  BLOG文档结构图       一.2  前言部分   一.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你 ...

  10. Hibernate配置文件模版

    hibernate.cfg.xml 配置文件模版: <?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-config ...