尽管可以从大多数应用程序异常中恢复,但不能从大多数运行时异常中恢复。

然后沿堆栈向上传递,直到应用程序处理它或程序终止。

.NET Framework 中的异常处理异常与传统的错误处理方法


运行时实现的异常处理具有以下特点:

  • 处理异常时不用考虑生成异常的语言或处理异常的语言。

  • 异常处理时不要求任何特定的语言语法,而是允许每种语言定义自己的语法。

  • 允许跨进程甚至跨计算机边界引发异常。

最后,运行时的异常处理比基于 Windows 的 C++ 错误处理更快。

非托管代码可以同时包含 C++ 样式的 SEH 异常和基于 COM 的 HRESULT。

.NET Framework 中的异常处理运行时如何管理异常


Exception 对象来表示该异常。

仅在异常发生时使用资源。

异常信息表对于受保护的块有四种类型的异常处理程序:

  • finally 处理程序,每当块退出时它都会执行,而不论退出是由正常控制流引起的还是由未经处理的异常引起的。

  • 错误处理程序,它在异常发生时必须执行,但在正常控制流完成时不执行。

  • 类型筛选的处理程序,它处理指定类或该类的任何派生类的任何异常。

  • 用户筛选的处理程序,它运行用户指定的代码,来确定异常应由关联的处理程序处理还是应传递给下一个受保护的块。

When 关键字)提供对用户筛选的处理程序的访问;C# 不实现用户筛选的处理程序。

异常发生时,运行时开始执行由下列两步组成的过程:

  1. 运行时在数组中搜索执行下列操作的第一个受保护块:

    • 保护包含当前执行的指令的区域。

    • 包含异常处理程序或包含处理异常的筛选器。

  2. 还请注意,异常处理程序可以访问捕捉异常的例程的局部变量和本地内存,但引发异常时的任何中间值都会丢失。

    如果没有该事件的侦听器,则运行时转储堆栈跟踪并结束应用程序。

     

    备注:

    CLR如何捕获并处理异常

    http://www.cnblogs.com/bitfan/archive/2009/12/08/1616550.html

     

           对于任何一个.NET应用程序中的类,其所包容的方法都包容着一个异常处理表,如果此方法中没有使用try…catch…finally,则此表为空(即此方法生成的IL指令中不包容任何的异常处理子句)。

     

           .NET应用程序运行时,如果正在执行的某个方法引发了一个异常,CLR会首先将相应的异常对象推入计算堆栈,然后扫描此方法所包容的异常处理表查找处理程序,其处理过程可以简述如下:

     

           CLR获取引发异常的IL指令地址,然后从上到下地扫描异常处理表,取出每个catch子句中“.try”关键字后面跟着的用于定位“块”的起始和结束地址,判断一下引发异常的IL指令地址是否“落”入此地址范围中。如果是,取出“catch”关键字后跟着的异常类型,比对一下是否与抛出的异常对象类型一致(或相兼容),如果这个条件得到满足,CLR取出handler后的两个IL地址,“准备”执行这两个地址指定范围的IL指令(这就是catch指令块中的异常处理代码)。

     

           如果本方法所包容的异常处理表中找不到合适的catch子句,CLR会依据引发异常的线程所关联的方法调用堆栈,查找此方法的调用者所包容的异常处理表。

     

           此过程将一直进行下去,直到找到了一个可以处理此异常的处理程序为止。

     

           假设CLR在整个方法调用链的某个“环节”(即调用此方法的某个“祖先”方法)所包容的异常处理表中找到了可处理此异常的catch异常处理子句,它就作好了执行此子句所定义的异常处理指令代码块的“准备”。

     

           “扫描并查找相匹配的catch子句”过程,是CLR异常处理流程的第一轮。

     

           当找到了合适的异常处理代码后,CLR再“回到原地”,再次扫描引发异常方法所包容的异常处理表,这回,CLR关注的不再是catch子句,而是finally子句,如果找到了合适的finally子句(只需判断一下引发异常的IL指令地址是否“落入”某finally子句所监视的IL指令地址范围之内即可),CLR执行finally子句所指定的处理指令(即其handler部分所定范围内的IL指令)。

     

           “扫描并查找相匹配的finally子句”过程,是CLR处理异常流程的第二轮。

     

           这“第二轮”的扫描,开始于引发异常的方法,结束于最顶层的包容了那个引发异常的方法的方法(这句话很拗口,举个例子就清楚了,比如,如果你有一个嵌套了很深的函数调用语句,并且在被调用的最底层的函数中引发了异常,而你在顶层Main()函数中又用try...catch...finally包围了这一函数调用语句,则第2轮扫描会“直达”最顶层Main()方法的异常处理表,不会中途停止于找到了合适catch子句的那个中间“站”。

     

      在所有“下层”finally子句执行结束之后,相应的catch子句所指定的异常处理代码块才开始执行。之后,与此catch子句“同层”的finally子句所指定的异常处理代码块得到执行。

     

         但事情还没完,现在轮到所有包容被执行catch子句所在方法的“父辈”方法中的finally子句执行。

     

           经过两轮的扫描,CLR就完成了对.NET应用程序引发异常的捕获与处理工作。

     

           这里还遗留着一个问题:

     

           CLR找不到合适的catch异常处理子句怎么办?

     

           如果某.NET应用程序中根本没有定义处理某种异常类型的代码,而此程序在运行时又真的引发了这种类型的异常(真是哪壶不开提哪壶),那么CLR在第一轮扫描过程中,会一直“上溯”到Main()方法所包容的异常处理表,然后“无功而返”。

     

           紧接CLR会进行第二轮的扫描,执行所有“应该被执行”的finally子句。

     

           故事的尾声是:在执行完了所有finally代码后,CLR强制中止此进程所创建的所有线程(哪怕它们运行正常),由操作系统显示一个“出错”对话框,等用户响应后,或结束或附加一个调试器来调试这个进程。

相关文章:

  • 2022-12-23
  • 2021-08-13
  • 2022-12-23
  • 2022-01-08
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-02-01
  • 2021-08-17
  • 2022-12-23
  • 2021-05-27
  • 2022-01-24
相关资源
相似解决方案