结构化异常处理(五)处理多个异常

一、多个catch块

在最简单的情形下,一个try块有一个catch块。但是在现实中,你常常会遇到包含try块的语句能够触发许多可能发生的异常的情形。

1.接之前的例子修改,假定在用户传入一个无效参数(比如小于0的值)的情况下,修改Car的Accelerate()方法还会引发一个基础类库定义的异常ArgumentOutOfRangeException。

该异常类的构造函数接收的第一个字符串参数为错误参数的名称,然后是描述该错误的信息。

代码:

1 public void Accelerate(int delta)
2 {
3     if (delta < 0)
4     {
5         throw new ArgumentOutOfRangeException("delta", "Speed must be greater than zero!");
6     }
7     ......
8 }

catch逻辑现在可以为每种异常分别作出回应:

 1 static void Main(string[] args)
 2 {
 3     Car myCar = new Car("Rusty", 90);
 4
 5     try
 6     {
 7         myCar.Accelerate(-10);
 8     }
 9     catch (CarIsDeadException e)
10     {
11         Console.WriteLine(e.Message);
12     }
13     catch (ArgumentOutOfRangeException e)
14     {
15         Console.WriteLine(e.Message);
16     }
17     Console.ReadKey();
18 }

输出:

Speed must be greater than zero!
参数名: delta

2.当创建多个catch块的时候,必须注意一个异常引发后都将被第一个可用的catch处理。

为了阐明什么是第一个可用的catch块,看代码:

如上图所示,我们试图处理通过捕获普通System.Exception来处理包括CarIsDeadException和ArgumentOutRangeException在内的所有异常,这段异常处理逻辑产生了编译错误。问题出在(is-a关系)第一个catch块可以处理任何派生自System.Exception的异常,其中包括CarISDeadException和ArgumentOutOfRangeException,故而最终导致永远无法到达另外两个catch块!

catch块按照下面的原则结构化:

最前面的catch块捕获最特定的异常(最顶端的子类异常),最后面的catch捕获最普通的异常(异常基类)。往往在最后一个catch块中才处理System.Exception。

下面是正确的代码:

 1 static void Main(string[] args)
 2 {
 3     Car myCar = new Car("Rusty", 90);
 4
 5     try
 6     {
 7         myCar.Accelerate(-10);
 8     }
 9     catch (CarIsDeadException e)
10     {
11         Console.WriteLine(e.Message);
12     }
13     catch (ArgumentOutOfRangeException e)
14     {
15         Console.WriteLine(e.Message);
16     }
17     catch (Exception e)
18     {
19         //处理其他的所有异常
20         Console.WriteLine(e.Message);
21     }
22     Console.ReadKey();
23 }

二、通用的catch语句(不推荐使用)

C#也支持通用catch块,它不显式接收由指定成员引发的一场对象:

代码:

 1 static void Main(string[] args)
 2 {
 3     Car myCar = new Car("Rusty", 90);
 4
 5     try
 6     {
 7         myCar.Accelerate(90);
 8     }
 9     catch
10     {
11         Console.WriteLine("Something bad happened...");
12     }
13     Console.ReadKey();
14 }

很明显,这不是处理异常的最佳途径,因为我们无法获取关于发生错误有意义的资料,因此不推荐使用。不过在C#中这样的构造确实是允许的,通过它可以用通用模式处理所有异常。

三、再次引发异常

可以在try块逻辑中向之前的调用者再次引发一个调用栈异常。要像这样,仅仅在catch块中使用throw关键字就行了,它通过调用逻辑链传递异常。

在catch块只能处理即将发生的部分错误时这样做很有用:

 1 static void Main(string[] args)
 2 {
 3     Car myCar = new Car("Rusty", 90);
 4
 5     try
 6     {
 7         myCar.Accelerate(90);
 8     }
 9     catch (CarIsDeadException e)
10     {
11         Console.WriteLine("Something bad happened...");
12         throw;
13     }
14     Console.ReadKey();
15 }

运行结果:

先出现

再出现

说明:

1.CarIsDeadException传给了CLR,该异常由Main()方法再次引发。这样的话,最终用户将看到一个系统提供的错误对话框。通常情况下,只需要向调用者再次引发部分处理过的异常,使之能够恰当地处理将发生的异常。

2.这里没有显式重新跑出CarIsDeadException对象,而是使用了不带参数的throw关键字。这里并没有创建新的异常对象,只是重新抛出了原始的异常对象。这样做保护了原始目标的上下文。

三、内部异常

我们完全可以在处理其他异常的时候触发一个异常。例如,假设我们在一个特定的catch块中处理CarIsDeadException,在这个过程中视图将栈跟踪记录到C盘下名为carError.txt的文件中:

1 catch (CarIsDeadException e)
2 {
3     FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
4     ...
5 }

如果指定的文件在C盘中不存在,调用File.Open()将导致一个FileNotFoundException!

如果在处理一个异常的时候遇到另外一个异常,最好的习惯是将这个新异常对象标识为与第一个异常类型相同得新对象中的”内部错误“。

我们之所以需要创建一个异常的新对象来等待处理,是因为声明一个内部错误的唯一途径就是将其作为一个构造函数参数。

原因:

在System.Exception基类中public Exception InnerException{ get; }是只读的无法赋值。但存在构造函数public Exception(string message, Exception innerException),因此声明一个内部错误的唯一途径就是就将其作为一个构造函数参数。

如下:

 1 catch (CarIsDeadException e)
 2 {
 3     try
 4     {
 5         FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open);
 6     }
 7     catch (Exception e2)
 8     {       //引发记录新异常的异常,还有第一个异常的相关信息
 9         throw new CarIsDeadException(e.Message, e2);
10     }
11 }

说明:

1.在上例中,我们将FileNotFoundException对象作为CarIsDeadException的第二个参数传递进来。一旦确定了这个新对象,我们就向下一个调用者的栈引发异常,本例中下一个调用者是Main()方法。

2.既然Main()方法没有下一个调用者来捕获异常,我们需要通过错误对话框将其再次呈现出来。就像再次引发异常一样,记录内部异常通常仅仅在调用者能够在首次发生异常时将其更恰当地捕获处理的情况下才有用。

例如,在本例中如果调用者catch块逻辑能够利用InnerException属性来获取内部异常对象的详细信息的话,记录内部异常才有用。

四、finally块

一个try/catch块后面可能接着会定义一个finally块。finally块并不是必须有的,它是为了保证不管是否有异常,一组代码语句始终都能被执行。

使用:在更加现实的场景中,当读者准备进行销毁对象、关闭文件、断开数据库连接等操作时,将资源清理加入到finally快中确保操作正确执行。

结构化异常处理(五)处理多个异常,布布扣,bubuko.com

时间: 2024-10-11 12:08:40

结构化异常处理(五)处理多个异常的相关文章

结构化异常处理系列

最近在项目中看到了许多自定义异常类,就想把它搞明白. 想知道为什么要这样处理?这样处理的优点是什么?自己怎么常见自己需要的自定义异常类? C#结构化异常处理是将程序中出现的错误的名称.消息和其他有用的信息都打进一个定义明确的包内. 构建一个强类型的自定义异常,这样在程序中对异常处理时就可以根据不同问题具有针对性的使用异常类,这样使用起来也更加明了,一看到就知道这里处理的是什么异常,一目了然. 下面就将学习笔记放在这里,希望大家多多指点: 结构化异常处理(一)概述 结构化异常处理(二)配置异常的状

[转载]深入解析结构化异常处理

尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的只是皮毛,一直对Windows异常处理的原理似懂非懂, 看了下面的文章 ,一切都豁然开朗. 1997年文章,Windows技术的根一直没变:http://www.microsoft.com/msj/0197/exception/exception.aspx Matt Pietrek 著 董岩 译 在Win32操作系统提供的所有功能中,使用最广泛而又没有公开的恐怕要数结构化异常处理(Structured Exception Ha

深入研究 Win32 结构化异常处理(好多相关文章)

摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的最基本概念讲起. Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理 在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了.一提到结构化异常处理,可能就会令人想起

深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的最基本概念讲起. Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理 在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了.一提到结构化异常处理,可能就会令人想起

结构化异常处理(三)系统级异常和应用程序级异常

一.系统级异常 1.准去的说,.NET平台引发的一场应被称为系统异常.这些异常被认为是无法修复的致命错误. 2.系统异常直接派生自名为System.SystemException的基类,该基类派生自System.Exception. SystemException除了一组自定义的构造函数不添加任何功能. public class SystemException :  Exception { //各种构造函数 } 3.当一个异常类型派生自System.SystemException时,我们就能够判

结构化异常处理(二):配置异常的状态

一.TargetSite属性(public MethodBase TargetSite { get; }) System.Exception.TargetSite属性帮助我们了解引发某个异常的方法的各种信息.输出TargetSite的值将显示返回值类型.方法名称.引发异常方法的参数. 它不是只返回字符串,而是返回一个强类型的System.Reflection.MethodBase对象. 1 Console.WriteLine("Member name: {0}", e.TargetSi

结构化异常处理(一)

一.错误.bug与异常 在.NET术语命名中,“异常”解释为bug.用户输入错误和运行时错误. 二..NET异常处理的作用 理想情况下,我们希望将这个错误的名称.消息和其他的有用信息都打进一个明确的包内,这正是结构化异常处理所做的. 结构化异常处理方案的优点: 1.开发人员现在又了统一的而且对.NET领域内各种语言都通用的方式来处理错误. 2.用以引发核捕获异常的语法在不同程序集间或计算机间都是一致的. 3.我们不再是通过接收意义模糊的数字常量来确定问题,而是可以通过异常,它包含容易度读懂的问题

第23章 SEH结构化异常处理(1)

23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制:SEH和VEH(WindowsXP以上新引进) (3)结构化异常处理(SEH)是Windows操作系统提供的强大异常处理功能.而Visual C++中的__try{} __finally{}和__try{} __except{}结构本质上是对Windows提供的SEH的封装. 23.1.2 SEH的

结构化异常处理(通过设置CONTEXT结构实现反跟踪)

结构化异常处理是Windows操作系统处理程序错误或异常的技术.在一个应用程序发生错误时,Windows挂起程序,并交由调试器处理,当调试器不能处理时,则调用程序的线程相关的SEH异常处理函数. 异常处理函数可以有一个或多个,采用链表的结构将他们链接起来.当前的处理函数如果不处理,并且有多个处理函数时,可以交由链起来的其它异常处理过程进行处理. 如果程序线程的异常处理函数均选择不处理,如果处于被调试状态,操作系统挂起程序通知调试器. 这时有两种可能: (1)如果程序未处于被调试状态或者调试器仍然