捕获异常

.NET提供了大量的预定义基类异常对象,本节介绍如何在代码中使用它们捕获错误情况。为了在C#代码中处理可能的错误情况,一般要把程序的相关部分分成3种不同类型的代码块:

  • try块包含的代码组成了程序的正常操作部分,但这部分程序可能遇到某些严重的错误。
  • catch块包含的代码处理各种错误情况,这些错误是执行tyr块中的代码时遇到的。这个块还可以用于记录错误。
  • finally块包含的代码清理资源或执行通常要在try块或catch快末尾执行的其他操作。无论是否抛出异常,都会执行finally块,理解这一点非常重要。因为finally块包含了应总是执行的清理代码,如果在finally块中放置了return语句,编译器就会标记一个错误。例如,使用finally块时,可以关闭在try块中打开的连接。finally块是完全可选的。如果不需要清理代码(如删除对象或关闭已打开的对象),就不需要包含此块。

下面的步骤说明了这些块是如何组合在一起捕获错误情况的:

(1) 执行的程序流进入try块。

(2) 如果在try块中没有错误发生,在块中就会正常执行操作。当程序流到达try块的末尾后,如果存在一个finally块,程序流就会自动进入finally块(第(5)步)。但如果在try块中程序流检测到一个错误,程序流就会跳转到catch块(第(3)步)。

(3) 在catch块中处理错误。

(4) 在catch块执行完后,如果存在一个finally块,程序流就会自动进入finally块:

(5) 执行finally块(如果存在)。

用于完成这些任务的C#语法如下所示:

            try
            {
                //code for normal execution
            }
            catch
            {
                //error handling
            }
            finally
            {
                //clean up
            }

实际上,上面的代码还有几种变体:

  • 可以省略finally块,因为它是可选的。
  • 可以提供任意多个catch块,处理不同类型的错误。但不应包含过多的catch块,以防降低应用程序的性能。
  • 可以定义过滤器,其中包含的catch块仅在过滤器匹配时,捕获特定块中的异常。
  • 可以省略catch块。此时,该语法不是标识异常,而是一种确保程序流在离开tyr块后执行finally块中的代码的方式。如果在try块中有几个出口点,这很有用。

这看起来不错,实际上是有问题的。如果运行try块中的代码,则程序流如何在错误发生时切换到catch块?如果检测到一个错误,代码就执行一定的操作,称为"抛出一个异常";换言之,它实例化一个异常对象类,并抛出这个异常:

throw new OverflowException();

这里实例化了OverflowException类的一个异常对象。只要应用程序在try块中遇到一条throw语句,就会立即查找与这个try块对应的catch块。如果有多个与try块对应的catch块,应用程序就会查找与catch块对应的异常类,确定正确的catch块。例如,当抛出一个OverflowException异常对象时,执行的程序流就会跳转到下面的catch块:

            catch(OverflowException ex)
            {
                //exception handling here
            }

换言之,应用程序查找的catch块应表示同一个类(或基类)中匹配的异常类实例。

有了这些额外的信息,就可以扩展刚才介绍的try块。为了讨论方便,假定可能在try块中发生两个严重错误:溢出和数组超出范围。假定代码包含两个布尔变量Overflow和OutOfBounds,它们分别表示这两种错误情况是否存在。我们知道,存在表示溢出的预定义溢出异常类OverflowException;同样,存在预定义的IndexOutOfRangeException异常类,用于处理超出范围的数组。

现在,try块如下所示:

            bool Overflow = true;
            bool OutOfBounds = true;
            try
            {
                //code for normal execution
                if(Overflow == true)
                {
                    throw new OverflowException();
                }
                //more processing
                if(OutOfBounds == true)
                {
                    throw new IndexOutOfRangeException():
                }
                //otherwise continue normal execution
            }
            catch(OverflowException ex)
            {
                //error handling for the overflow error condition
            }
            catch(IndexOutOfRangeException ex)
            {
                //error handling for the index out of range error contition
            }
            finally
            {
                //clean up
            }

这是因为throw语句可以嵌套在try块的几个方法调用中,甚至在程序流进入其他方法时,也会继续执行 一个try块。如果应用程序遇到一条throw语句,就会立即退出栈上所有的方法调用,查找try块的结尾和合适的catch块的开头,此时,中间方法调用中的所有局部变量都会超出作用域。try...catch结构最适合于本节开头描述的场合:错误发生在一个方法调用中,而该方法调用可能嵌套了15或20级,这些处理操作会立即停止。

从上面的论述可以看出,try块在控制执行的程序流上有重要的作用。但是,异常是用于处理异常情况的,这是其名称的由来,不应该用异常来控制退出do...while循环的时间。

异常和性能

异常处理具有性能含义。在常见的情况下,不应该使用异常处理错误。例如,将字符串转换为数字时,可以使用int类型的Parse方法。如果传递给此方法的字符串不能转换为数字,则此方法抛出FormatException异常;如果可以转换一个数字,但它不能放在int类型中,则抛出OverflowException异常:

        static void NumberDemo1(string n)
        {
            if(n is null) throw new ArgumentNullException(nameof(n));
            try
            {
                int i = int.Parse(n);
                System.Console.WriteLine($"converted: {i}");
            }
            catch(FormatException ex)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值