C# 图解教程 第5版 —— 第23章 异常

文章目录

    • [23.1 什么是异常](#23.1 什么是异常)
    • [23.2 try 语句](#23.2 try 语句)
    • [23.3 异常类](#23.3 异常类)
    • [23.4 catch 子句](#23.4 catch 子句)
    • [23.5 异常过滤器](#23.5 异常过滤器)
    • [23.6 catch 子句段](#23.6 catch 子句段)
    • [23.7 finally 块](#23.7 finally 块)
    • [23.8 为异常寻找处理程序](#23.8 为异常寻找处理程序)
    • [23.9 进一步搜索](#23.9 进一步搜索)
      • [23.9.1 一般法则](#23.9.1 一般法则)
      • [23.9.2 搜索调用栈的示例(*)](#23.9.2 搜索调用栈的示例(*))
    • [23.10 抛出异常](#23.10 抛出异常)
    • [23.11 不带异常对象的抛出](#23.11 不带异常对象的抛出)
    • [23.12 throw 表达式](#23.12 throw 表达式)

23.1 什么是异常

​ 异常是程序中的运行时错误,它违反了系统约束或应用程序约束,或是正常操作时不会发生的状况。如果程序没有提供处理异常的代码,系统会挂起这个程序。例如,下面的代码在试图用 0 除一个数时抛出一个异常:

​ 在没有异常处理程序的情况下,应用程序将停止(或者崩溃),并向用户显示非常不友好的错误消息。异常处理的目标是通过以下操作来响应异常:

  1. 在有限的几种情况下采取纠正措施,让应用程序继续运行。
  2. 记录有关异常的信息,以便开发团队可以解决该问题。
  3. 清理任何外部资源,例如可能保持打开的数据库连接。
  4. 向用户显示友好的信息。

23.2 try 语句

​ try 语句用来指明为避免出现异常而被保护的代码段,并在发生异常时提供代码处理。其包含 3 个部分组成:

  1. try 块。
  2. catch 子句。
  3. finally 块。

图23.1 try 语句的结构

处理异常

​ 将上述代码段放在一个 try 块中,并提供一个简单的 catch 子句来捕获并处理异常。

23.3 异常类

​ BCL 定义了许多异常类,每一个类代表一种指定的异常类型。当一个异常发生时,CLR 创建该类型的异常对象并寻找适当的 catch 子句以处理它。

​ 所有异常类都派生自 System.Exception 类,System.Exception 类派生自 System.Object 类。
图23.2 异常层次的结构

​ 异常对象含有只读属性,该属性提供有助于调试应用程序的异常信息。
表23.1 异常对象的一部分属性

23.4 catch 子句

​ catch 子句处理异常,有如下 4 种形式:
图23.3 catch 子句的 4 种形式

  • 一般 catch 子句(形式 1):能接受任何异常,但不确定引发异常的异常类型,只能进行普通处理和清理。
  • 特定 catch 子句(形式 2):把一个异常类的名称作为参数,匹配指定类或派生自它的异常类的异常。
  • 带对象的特定 catch 子句(形式 3、4):提供的异常信息最多,可以在 catch 子句块内部访问异常变量的属性,以获取异常的详细信息。

23.5 异常过滤器

​ 形式 4 的 catch 子句是在 C# 6.0 中添加的,相较于形式 3,异常对象还需满足特定条件,该条件被称为过滤器。这允许程序员编写更小、更专一的异常处理程序,而无需再单个处理程序中包含大量 if 语句。

​ 有关 when 子句的重要特征如下:

  • 必须包含谓词表达式(返回值为 true 或 false)。
  • 不能是异步的。
  • 不应使用任何需要长时间运行的操作。
  • 谓词表达式中发生的异常会被忽略。

23.6 catch 子句段

​ catch 子句段可以包含多个 catch 子句。
图23.4 try 语句的 catch 子句段结构

​ 当发生异常时,系统按顺序搜索 catch 子句的列表,第一个匹配该异常对象类型的 catch 子句被执行。

  • catch 子句必须以特定顺序排列。最特定的异常类型排第一,最普通的类型排最后。
  • 如果有一个一般 catch 子句,则必须放在最后一个。
  • 不鼓励使用一般 catch 子句,因为它允许程序继续执行从而隐藏了特定错误,让程序处于一种位置的状态。

23.7 finally 块

​ 如果程序的控制流进入了带 finally 块的 try 语句,那么 finally 始终会被执行。
图23.5 finally 块的执行

​ 即是 try 块中有 return 语句,或在 catch 块中抛出一个异常,finally 块也总是会在返回到调用代码之前执行。

​ 上述代码在 inVal 值为 5 时也会打印 finally 块中的语句。

23.8 为异常寻找处理程序

​ 当程序抛出异常时,系统查看该程序是否提供了异常处理程序,具体流程如下:

  • 如果在 try 块内发生了异常,系统会查看是否有任何一个 catch 子句能处理该异常。
  • 如果找到了适当的 catch 子句:
    1. 该 catch 子句被执行。
    2. 如果有 finally 块,那么它被执行;否则,继续在最后一个 catch 子句之后执行。

图23.6 在当前 try 语句中有处理程序的异常

23.9 进一步搜索

​ 如果异常在一个没有被 try 语句保护的代码段抛出,或者如果 try 语句没有匹配的异常处理程序,系统将不得不进一步寻找匹配的处理程序。即,按顺序搜索调用栈,查看是否存在带匹配的处理程序的封装 try 块。

​ 如果异常发生在 Method2 内的 try 块内部,系统会执行如下操作:

  1. 首先查看 Method2 是否有能处理该异常的异常处理程序:
    • 如果有,Method2 处理,程序继续执行。
    • 否则,系统沿着调用栈找到 Method1,搜寻适当的处理程序。
  2. 如果 Method1 有一个适当的 catch 子句,那么系统将:
    • 回到栈顶,即 Method2 处。
    • 执行 Method2 的 finally 块,并将 Method2 弹出栈。
    • 执行 Method1 的 catch 子句和 finally 块。
  3. 如果 Method1 没有适当的 catch 子句,系统继续搜索调用栈。

图23.7 搜索调用栈

23.9.1 一般法则

图23.8 处理异常的一般法则

23.9.2 搜索调用栈的示例(*)

23.10 抛出异常

​ 使用 throw 语句使代码显示抛出异常,throw 语句的语法如下:

​ 下面的代码在 try 块中进行参数 null 检查,创建并抛出 ArgumentNullException 异常。该实例在 catch 语句中被捕获,错误被打印出来。

23.11 不带异常对象的抛出

​ throw 语句可以在 catch 块内部不带异常对象使用。

  • 该形式重新抛出当前异常,系统会继续搜索,为该异常寻找另外的处理程序。
  • 这种形式只能用在 catch 语句内部。

23.12 throw 表达式

​ 代码中有些地方不允许使用语句,而只能使用表达式。C# 7.0 后,可以在只能应用表达式的地方使用 throw 表达式,其语法和 throw 语句相同。

相关推荐
煤泥做不到的!1 小时前
挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
开发语言·c++
F-2H1 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
bryant_meng2 小时前
【python】OpenCV—Image Moments
开发语言·python·opencv·moments·图片矩
若亦_Royi3 小时前
C++ 的大括号的用法合集
开发语言·c++
资源补给站4 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
m0_748247554 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
测试杂货铺4 小时前
Jmeter压测实战:Jmeter二次开发之自定义函数
自动化测试·软件测试·测试工具·jmeter·职场和发展·测试用例·压力测试
6.944 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
FF在路上5 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
呆呆的猫5 小时前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展