发生异常的最终方法都是终止当前函数的调用。
1 异常基础知识
异常是指程序运行时出现的不正常的为,可能是程序错误,也可能是自定义逻辑错误,还有可能是其它不正常的行为。
异常处理包括:异常的定义、异常的捕获、异常的处理。
异常的处理机制是使用try、catch 和 finally 关键字进行某些操作。将可能出现异常的语句放在 try 语句块中,并当发生异常时,使用 throws 抛出异常实例;使用 catch 语句捕获异常实例,并对异常进行处理;finally 语句用于清理在 try 语句块中执行的操作。
异常的类型有由执行程序生成的异常和由公共语言运行时生成的异常,而需要由用户处理的异常全部包含在Exception 类或其派生类中。并且系统给出了常见的若干种异常类,如下所示。
ArithmeticException
在算术运算期间发生的异常(如 DivideByZeroException 和 OverflowException)的基类。
ArrayTypeMismatchException
当数组存储给定的元素时,如果由于该元素的实际类型与数组的实际类型不兼容而导致存储失败,就会引发此异常。
DivideByZeroException
在尝试用零除整数值时引发。
IndexOutOfRangeException
在尝试为数组设置小于零或超出数组界限的索引时引发。
InvalidCastException
当从基类型到接口或派生类型的显式转换在运行时失败时,就会引发此异常。
NullReferenceException
在尝试引用值为 null 的对象时引发。
OutOfMemoryException
在使用 new 运算符分配内存的尝试失败时引发。这表明可用于公共语言运行时的内存已耗尽。
OverflowException
在 checked 上下文中的算术运算溢出时引发。
StackOverflowException
当执行堆栈由于具有太多的挂起方法调用而耗尽时,就会引发此异常;这通常表明存在非常深的递归或无限递归。
TypeInitializationException
在静态构造函数引发异常并且不存在可以捕捉到它的兼容 catch 子句时引发。
2 几种异常处理情形
2.1 由系统抛出系统定义异常
系统定义的上述几种常见异常全部继承与 Exception 类,而具体是由公共语言运行时(CLR)抛出相应的异常对象,并由系统定义的异常处理程序接收对象并进行处理,处理方式一般为弹出异常对话框,其中有关于异常的信息。
示例如下。
int a = 1, b = 0;
int result = a / b;
当执行第二条语句时,系统将会中断当前程序,并弹出异常处理对话框,并显示。未经处理的异常:System.DivideByZeroException:尝试除以零。以及相应异常程序位置。
一般来说这种情形的异常,是不需用户去定义的,而需要做的是发现这种异常,并使用异常处理机制去处理,从而使得程序能够正常终止。
2.2 由用户抛出系统定义异常
用户需要了解系统定义的常见异常,并在适当的时候抛出这种异常,抛出异常需要使用 throw 关键字。
示例如下。
class Program
{
static double SafeDivision(double x, double y)
{
if (y == 0)
throw new System.DivideByZeroException();
return x / y;
}
static void Main(string[] args)
{
int a = 1, b = 0;
try
{
Console.WriteLine("{0} divide {1} equal to {2}", a, b, SafeDivision(a, b));
}
catch (DivideByZeroException e)
{
Console.WriteLine("try divide zero");
}
}
}
在 SafeDivision()方法中抛出异常类对象,如果用户仅只抛出异常对象,而不使用 try、catch 块捕获对象,那么抛出的异常对象就由系统接收处理,处理的方式是提出异常对话框,接着结束程序流程。
Catch块捕获异常对象的机制是从第一个 catch 块开始,依次往下开始匹配,当有匹配对象时,则执行该 catch 块中的处理程序,接着结束当前的匹配流程,也就是说只有第一个匹配的 catch 块会发生作用,这是使得定义 catch 块的顺序,有一定的要求,前面定义的 catch 匹配块中的匹配对象不能大于或等于后面定义的 catch 匹配块中的匹配对象,可以理解为派生类对象在前,基类对象在后。
2.3 由用户抛出自定义异常
虽然系统定义了许多异常类,这些类对于关于系统的程序运行时发生违规操作一般都可以处理,但某些系统某些逻辑功能,则需要用户定义相应的异常类去处理相关状况。
如下所示,对于银行的储户,当其取款数目大于存款数目,则需要终止该种行为。
class Program
{
static private double account = 0;
public void set_Account(double d)
{
account += d;
}
public void get_Account(double d)
{
if (d > account)
{
throw new MyException("get number above account number");
}
else
{
account -= d;
}
}
static void Main(string[] args)
{
Program people = new Program();
people.set_Account(100);
try
{
people.get_Account(200);
}
catch (MyException e)
{
Console.WriteLine(e.Message);
}
}
}
public class MyException : Exception
{
public MyException(string s)
: base(s)
{
}
}
执行程序输出:getnumber above account number。因为只存入100元,而尝试取出200,引发自定义异常。
3 异常处理的程序流程
将可以发生异常的语句放在try 语句块中,当 try 语句块在执行时发生异常,则跳出当前的方法(不管调用的级别多深,都是在 try 语句块中的发生异常的方法),进而跳转到 catch 块进行匹配,如果匹配则执行第一个匹配的 catch 块,如果不匹配,将继续跳出当前的方法,并尝试跳转到 catch 块进行匹配,如果都没有匹配,则由系统定义的处理程序处理(当且仅有一个 catch块成功匹配,并进行处理),在处理完成后(即便是由系统定义的处理程序处理)将跳转至 finally 语句块中,并执行其中的语句。
在处理异常时,如果需要释放资源(如如文件流、数据库连接和图形句柄),则需要使用 finally 关键字进行定义,其不论是否发生异常,都将被执行。
4 参考
C# 编程指南
C#自学手册