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

发布时间:2024年01月13日

23.1 什么是异常

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

image-20240113130921192 image-20240113130931943

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

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

23.2 try 语句

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

  1. try 块。
  2. catch 子句。
  3. finally 块。
image-20240113131311982
图23.1 try 语句的结构

处理异常

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

image-20240113131404143 image-20240113131414697

23.3 异常类

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

? 所有异常类都派生自 System.Exception 类,System.Exception 类派生自 System.Object 类。

image-20240113131616192
图23.2 异常层次的结构

? 异常对象含有只读属性,该属性提供有助于调试应用程序的异常信息。

表23.1 异常对象的一部分属性
image-20240113131830423

23.4 catch 子句

? catch 子句处理异常,有如下 4 种形式:

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

23.5 异常过滤器

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

image-20240113132555826

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

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

23.6 catch 子句段

? catch 子句段可以包含多个 catch 子句。

image-20240113133131889
图23.4 try 语句的 catch 子句段结构

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

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

23.7 finally 块

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

image-20240113133444181
图23.5 finally 块的执行

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

image-20240113133744787

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

image-20240113133821319

23.8 为异常寻找处理程序

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

  • 如果在 try 块内发生了异常,系统会查看是否有任何一个 catch 子句能处理该异常。
  • 如果找到了适当的 catch 子句:
    1. 该 catch 子句被执行。
    2. 如果有 finally 块,那么它被执行;否则,继续在最后一个 catch 子句之后执行。
image-20240113134246204
图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 子句,系统继续搜索调用栈。
image-20240113134534938
图23.7 搜索调用栈

23.9.1 一般法则

image-20240113134928143
图23.8 处理异常的一般法则

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

23.10 抛出异常

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

image-20240113135129478

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

image-20240113135200880 image-20240113135423860

23.11 不带异常对象的抛出

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

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

23.12 throw 表达式

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

image-20240113135912305 image-20240113135925872
文章来源:https://blog.csdn.net/zheliku/article/details/135569449
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。