CLR如何捕获并处理异常

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

根据之前的学习我们知道,当应用程序拥有多层嵌套的异常捕获结构时,如果最底层发生了异常,CLR将优先在引发异常的那一层去搜索catch语句块,看看有没有“兼容”此类型异常的处理代码,如果没有,跳到上一层去查找,如果上一层还没有,继续搜索上一层的上一层,直到应用程序的最顶层。

这就是CLR处理嵌套异常捕获结构应用程序的第一轮遍历——查找合适的异常处理程序。

如果在某一层中找到了异常处理代码,CLR并不会马上执行,而是回到事故现场,进行第二轮遍历,执行所有中间层的finally语句块,然后执行找到的异常处理代码,最后再从本层开始一直遍历到最顶层,执行所有的finally语句块。

这就是.NET应用程序异常处理策略的两轮遍历。

示例代码如下:

class ExceptionA : Exception
{
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            throw new ExceptionA();
        }
        catch (FormatException ex)
        {
            Console.WriteLine(ex.Message);
        }
        catch (NullReferenceException ex)
        {
            Console.WriteLine(ex.Message);
        }
        catch (ExceptionA ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            Console.WriteLine("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子句,判断一下引发异常的IL指令代码是否落入某个finally语句所监视的IL指令范围之内,CLR据此查找合适的finally语句并执行。

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

第二轮的扫描,开始于引发异常的方法,结束于找到异常处理代码的那一层的方法。然后开始执行异常处理代码,最后执行上一层的finally语句块,一直向上执行直到所有finally语句都执行结束。

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

但是,如果CLR并不是每次都能找到合适的异常处理代码,如果.NET应用程序中没有定义处理某种异常类型的代码,而程序运行时又恰好出现此类型的异常,那么CLR在第一轮扫描时一直上溯到Main方法所包含的异常处理表,然后无功而返。

紧接着CLR会进行第二轮的扫描,执行所有应该被执行的finally语句。在执行了完了finally语句后,CLR将控制权交给操作系统,由它强制中止此进程所创建的所有线程(即使线程本身运行正常),并显示一个出错的对话框后结束整个进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值