zoukankan      html  css  js  c++  java
  • [CLR via C#]异常和状态管理

          当CLR检测到某个正在运行的.NET应用程序处于一种特殊的正常执行顺序被打断的状态时,会生成一个异常对象来表示这个错误,并将此对象在方法调用堆栈中向上传送。如果一个程序引发了一个异常却没有处理,CLR将会中断此进程。

    一、异常处理机制

    1.基本语法

    try
    {
        //可能引发异常的语句
    }
    catch(Exception e)
    {
        //对异常进行处理的语句
    }
    finally
    {
        //"无论如何都要执行的语句"
    }

    2.finally语句块中也可能发生异常,如果这种情况发生,则先前的异常会被抛弃。如下示例

     class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    Method1();
                }
                catch (NotSupportedException ex)
                {
                    Console.WriteLine("Main NotSupportedException");
                }
            }
    
            private static void Method1() 
            {
                try
                {
                    Method2();
                }
                catch (IndexOutOfRangeException ex)
                {
                    Console.WriteLine("Method1 IndexOutOfRangeException");
                }
            }
    
            private static void Method2()
            {
                int[] numbers = new int[10];
    
                try
                {
                    for (int i = 1; i <= 10; i++)
                    {
                        Console.WriteLine(numbers[i]);
                    }
                }
                catch (IndexOutOfRangeException ex)
                {
                    Console.WriteLine("Method2 IndexOutOfRangeException");
    
                    throw;
                }
                finally
                {
                    throw new NotSupportedException("Method2 finally exception");
                }
            }
        }

    3.CLR的"两轮遍历"异常处理策略

          当应用程序拥有多层嵌套的异常捕获结构时,如果最底层或中间层发生了异常,CLR将优先在引发异常的那一层搜索catch语句块,看看有没有"兼容"此类型异常的处理代码,如果没有,"跳到"上一层去搜索,如果上一层还没有,继续往上搜索,由此直到方法调用堆栈的最顶层。这就是CLR异常处理策略的第一轮遍历:查找合适的异常处理程序。

          如果在某一层找到了异常处理程序,注意,CLR并不会马上执行之,而是回到"事故现场",再次进行第二轮遍历,执行所有"中间"层次的finally语句块,然后,执行找到的异常处理程序,最后,再从本层开始一直遍历到最顶层,执行所有的finally语句块。但是有一个问题,如果始终没有找到合适的异常处理程序会怎么样,你试试就知道了。

    二、catch的"诱惑"

    有的程序员害怕让用户看到异常的出现,于是把所有的异常隐藏起来。这种作法其实是应用了一种"鸵鸟"策略,据说当有危险降临而又无法挣脱时,鸵鸟就会将头插入沙中,欺骗自己安全了。现实应用中不建议采取这样的做法来处理异常。

    try
    {
        //功能代码
    }
    catch(Eexception ex)
    {
        //在此处"吃掉"所有异常,没有任何代码进行异常处理
    }

    三、CLR异常处理机制探秘

    1.方法的异常处理表

            static void Main(string[] args)
            {
                try
                {
                    int number = Convert.ToInt32(Console.ReadLine());
                }
                catch (FormatException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    Console.WriteLine("finally");
                }
            }

    使用ildasm工具反编译出来的代码框架如下所示:

    从上述代码中可知:

    C#编程语言中的单层的try.catch.finally结构会被转换为"两层嵌套"的类似结构,CLR通过执行leave指令在IL汇编程序的try、catch和finally指令块间跳转,实现所定义的异常捕获和处理逻辑。

    单击ildasm的"view"菜单,取消"expand try/catch"选项,可以看到C#编译器生成的IL代码的真面目。

    具体功能代码被统一地放置在方法IL代码的前半部分,而用于实现异常捕获的代码放在方法IL代码的后半部分,称为"异常处理表","ret"指令是两部分的分界线。C#编译器通过在合适的地方插入leave指令使得其在无异常的情况下,永远执行不到异常处理代码。异常处理表中的每一项代表一个异常处理子句,IL汇编程序使用try、catch、handler和finally关键字,配合相应地址对前面的功能代码自然分块。

    2.CLR如何捕获并处理异常

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

          当找到了合适的异常处理代码后,CLR再"回到原地",再次扫描引发异常方法所包容的异常处理表,这回CLR关注的不再是catch子句,而是finally子句,如果找到了合适的finally子句,CLR执行finally子句所指令的处理指令。"扫描并查找相匹配的finally子句"过程,是CLR处理异常流程的第二轮。

  • 相关阅读:
    Day 20 初识面向对象
    Day 16 常用模块
    Day 15 正则表达式 re模块
    D14 模块 导入模块 开发目录规范
    Day 13 迭代器,生成器,内置函数
    Day 12 递归,二分算法,推导式,匿名函数
    Day 11 闭包函数.装饰器
    D10 函数(二) 嵌套,命名空间作用域
    D09 函数(一) 返回值,参数
    Day 07 Day08 字符编码与文件处理
  • 原文地址:https://www.cnblogs.com/JustYong/p/4752678.html
Copyright © 2011-2022 走看看