System.Exception类
C#中定义了很多异常类,C#的异常类都是System.Exception
的子类。它派生了两个子类:SystemException
和ApplicationException
。其中前者是系统定义的各种异常,后者则提供应用程序使用。
Exception
的构造方法常用的有两个:
public Exception();
public Exception(string s);
第二个构造方法可以接受字符串参数传入的信息,该信息通常是对该例外所对应的错误的描述。Exception
类有两个重要的属性:Message
属性、StackTrace
属性。
Message属性: 描述错误的可读文本。在创建异常对象过程中,可以将文本字符串传递给构造方法以描述该特定异常的详细信息。
StackTrace属性 发生异常时调用堆栈的状态。包括错误发生位置的堆栈跟踪、所有调用的方法和源文件中这些调用所在的行号。
系统定义的异常
如下表:
捕获和处理异常
C#中的异常处理机制可以概括成以下几个步骤:
- ① C#程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给C#运行时的系统,这个过程称为抛出(throw)异常。抛出异常也可以有程序来强制的用
throw
语句来进行。 - ② 当C#运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
- ③ 如果C#运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的C#程序也将退出。
1.抛出异常 throw
C#程序在运行时如果引发了一个可识别的错误,就会产生一个与该错误相对应的异常类的对象,这个过程称为异常的抛出。
(1)系统自动抛出的异常
所有的系统定义的运行异常都可以由系统自动抛出。
(2)语句抛出的异常
用户程序自定义的异常不可能依靠系统自动抛出,而必须借助于throw
语句来定义何种情况算是产生了此类异常对应的错误,并且抛出这个异常类的新对象。格式为:
throw 异常对象;
2.捕获异常 catch
当一个异常被抛出时,应该有专门的语句来接收这个被抛出的异常对象,这个过程称为捕获异常。当一个异常类的对象被捕捉或接收后,用户程序就会发生流程的跳转,系统终止当前的流程而跳转至专门的异常处理语句块,或者直接跳出当前程序并终止。
在C#程序中使用catch
语句,其格式如下:
try {
语句组
} catch(异常类名 异常形式参数名) {
异常处理语句
} catch(异常类名 异常形式参数名) {
异常处理语句
} finally {
异常处理语句
}
其中至少有一个catch
语句或finally
语句。
finally语句: finally
语句为异常处理提供一个统一的出口,不论try
代码块及catch
块中是否发生了异常事件也不论出现任何语句(即使用return
或throw
抛出异常),finally块中的语句都会被执行。
用户自定义异常类
创建用户自定义异常时,一般需要完成如下的工作:
- ① 声明一个新的异常类,使之以
ApplicationException
类或其他某个已经存在的系统异常类或用户异常类为父类。 - ② 为新的异常类定义属性和方法,或重载父类的属性和方法,使这些属性和方法能够体现该类所对应的错误的信息。
例如:
public class MyAppException : ApplicationException
{
public MyAppException(string message) : base(message) { }
}
重抛异常及异常链接
系统中对于异常,可以进行捕获和处理,有时候不仅要处理,还要将此异常进一步传递给调用者,以便让调用者也能感受到这种异常。这时可以在catch语句块或finally语句块中采取以下三种方式:
- ① 使用不带表达式的
throw
语句,将当前捕获的异常再次抛出。格式如下:
throw;
- ② 重新生成一个异常,并抛出,如:
throw new Exception("some message");
- ③ 重新生成并抛出一个新异常,该异常中包含了当前异常的信息,如
throw new Exception("some message", e);
其中,最后一种方法比较好,因为它将当前异常的信息保留,并且向调用者返回了一个更有意义的信息。这种方式被称为“异常的链接”。如果相关的异常都采用这种方式,能够使上层的调用者逐步深入地找到相关的异常信息。
这种方式中,除了用到Exception
类的Message
、StackTrace
属性和ToString()
方法外,还常用到InnerException
属性。
InnerException
是一个只读属性,它包含这个异常的“内部异常”。如果它不是null
,就指出当前的异常是作为对另外一个异常的链接而被抛出。
为了对InnerException
属性进行指定,必须使用以下构造方法:
public Exception(string Message, Exception InnerException);
其中第二个参数指定了这个“内部异常”。如果是用户自定义的异常,也一般要提供一个构造方法,参数中能指定这个内部异常。
示例使用内部异常进行异常的链接:
using System;
public class DataHouse
{
public static void FindData(long ID) {
if (ID > 0 && ID < 1000)
Console.WriteLine(ID);
else
throw new DataHouseException("已收到文件尾");
}
}
public class BankATM
{
public static void GetBalanceInfo(long ID) {
try {
DataHouse.FindData(ID);
} catch (DataHouseException e) {
throw new MyAppException("账号不存在", e);
}
}
}
public class DataHouseException : ApplicationException
{
public DataHouseException(string message) : base(message) { }
}
public class MyAppException : ApplicationException
{
public MyAppException(string message) : base(message) { }
public MyAppException(string message, Exception inner) : base(message, inner) { }
}
class Program
{
static void Main(string[] args) {
Console.WriteLine("Hello World!");
try {
BankATM.GetBalanceInfo(12345L);
} catch (Exception e) {
Console.WriteLine("出现了异常: {0}", e.Message);
Console.WriteLine("内部原因: {0}", e.InnerException.Message);
}
}
}