异常处理
结构化异常处理是现代分布式环境下组件设计的一个必要的环节,.NET通用语言运行时从底层构造给予异常处理以坚实的支持。在C#中,异常对象被设计为封装了各种异常信息的类(System.Exception及其继承子类,和接口类似,它被推荐命名时加上后缀“Exception”,但这并非必须),“try-catch-finally”语句和异常对象一起为C#组件设计提供从异常侦测、异常捕捉和处理等一揽子服务。
我们将可能出现异常的语句放在try语句块内,这样,代码中出现的异常才可能被侦测到并传递给catch语句。catch语句可以带参数,也可以不带参数,catch语句所带参数类型只能为System.Exception及其继承子类。带参数的catch语句将只捕捉参数指定类型的异常,对于其他异常不予处理。不带参数的catch语句相当于参数为System.Exception类型的异常(即所有的异常),只不过不能获取异常变量。一个try语句块后面可以匹配多个catch语句,以捕捉不同的异常类型。这里必须注意的是多个catch语句的顺序,后面的catch语句中的参数类型不能是前面类型的子类,也不能和前面的参数类型相同。
如果catch语句的参数类型没有匹配try语句侦测到的异常类型,异常将在该语句的方法体内被抛出。如果调用该方法的方法没有相应的侦测和捕捉机制,那么异常继续被抛出,直到被捕捉,或者被抛出到应用程序的入口点Main函数,从而引起程序异常中断执行。这种异常抛出的机制对于在try语句之外出现的异常也同样适用——侦测到但没有捕捉到和没有侦测的效果一样!另外一点需要注意的是程序一旦发生异常,那么其后面的代码将不被执行!
但如果程序逻辑需要不管出现异常与否,都要执行某些任务紧急的代码(典型的如关闭打开的文件,网络端口等)怎么办呢?C#提供finally语句来解决这种情况。finally语句不能单独使用,它和try、catch有两种搭配情况: Try、catch、finally三者搭配构成try-catch-finally语句,在三者的搭配中,即使抛出了异常,finally语句也被执行; try语句还可以和finally语句搭配构成try-finally语句。try-finally语句侦测到异常后,只是简单地抛出,并没有捕捉。但在这种异常得到处理(被调用其方法的catch语句捕捉,或者到达Main函数入口点被执行环境中断)后,finally语句仍然执行。当然finally语句块只能有一个,这是不言自明的。
throw语句是用来抛出异常的,在代码设计中,遇到违背程序设计设定的条件,或异常的情况,我们常常要抛出特定的异常,这时候throw语句就很有用了,我们来看下面的例子:
using System;
public class MyException:ApplicationException {
public MyException (String message) : base (message) {}
public MyException (String message, Exception inner) : base(message,inner) {}
}
public class MyClass{
public static void ExMethod() {
throw new MyException(“Exception in ExMethod”); }
public static void MyMethod(){
try {ExMethod(); }
catch (Exception e) {
throw new MyException(“Exception in MyMethod”,e);}
}
}
public class Test {
public static void Main() {
try {
MyClass.MyMethod(); }
catch(Exception e) {
Console.WriteLine(e);
Console.WriteLine();
Console.WriteLine(e.GetBaseException());
}
}
}
程序输出:
MyException: Exception in MyMethod——> MyException: Exception in ExMethod
at MyClass.ExMethod()
at MyClass.MyMethod()
——End of inner exception stack trace——
at MyClass.MyMethod()
at Test.Main()
MyException: Exception in ExMethod
at MyClass.ExMethod()
at MyClass.MyMethod()
在上面的例子,我们设计了自己的异常类MyException,并在自己的设计类中简单地应用了这种异常类。测试程序中展示了它的处理,以及在多个方法调用中异常的追踪。其中e.GetBaseException()是调用System.Exception的GetBaseException()方法,获得引起异常e的最开始的那个异常,如果没有,将返回e自己。System.Exception的很多方法和属性为我们提供了很好的对异常的描述和追踪服务,它是我们应用异常、设计异常、认识异常的一个很好的起点。
Checked与Unchecked
对于因为整数类型参与算术操作和类型转换时产生的“溢出异常”——System.OverflowException,在某些算法来讲不算真正的“异常”,相反这种溢出常常为程序所用。C#通过引入checked和unchecked关键字来控制这种特殊情况的需求。它们都可以加于一个语句块前(如:checked{……}),或者一个算术表达式前(如:unchecked(x+y)),其中加checked标志的语句或表达式如果发生算术溢出,则抛出System.OverflowException类型的异常,而加unchecked标志的语句发生算术溢出时,则不抛出异常。下面是一个示例:
using System;
class Test{
static void Main() {
int num1=100000,num2=100000,
result=0;
checked{ try { result= num1 * num2;}
catch(System.Overflo2wException e){ Console.WriteLine(e); }
finally{ Console.WriteLine(result);}
}
unchecked{ try { result= num1 * num2;}
catch(System.OverflowException (e){ Console.WriteLine(e);}
finally{ Console.WriteLine(result);}
}
}
}
程序输出:
System.OverflowException: Arithmetic operation resulted in an overflow.
at Test.Main()
0
1410065408
可以看到同样的算术操作,用checked抛出了溢出异常,而unchecked只是将溢出的位丢弃而得到剩下的32位组成的十进制整数值。值得指出的是可以用“/checked”编译器选项指定整个文件的代码为checked语义,如果没有指定则默认为unchecked。如果同时在程序代码中指定checked或unchecked标志,又有了checked编译器选项,则除了标志为unchecked的代码外,其余的都有checked语义。
结构化异常处理是现代分布式环境下组件设计的一个必要的环节,.NET通用语言运行时从底层构造给予异常处理以坚实的支持。在C#中,异常对象被设计为封装了各种异常信息的类(System.Exception及其继承子类,和接口类似,它被推荐命名时加上后缀“Exception”,但这并非必须),“try-catch-finally”语句和异常对象一起为C#组件设计提供从异常侦测、异常捕捉和处理等一揽子服务。
我们将可能出现异常的语句放在try语句块内,这样,代码中出现的异常才可能被侦测到并传递给catch语句。catch语句可以带参数,也可以不带参数,catch语句所带参数类型只能为System.Exception及其继承子类。带参数的catch语句将只捕捉参数指定类型的异常,对于其他异常不予处理。不带参数的catch语句相当于参数为System.Exception类型的异常(即所有的异常),只不过不能获取异常变量。一个try语句块后面可以匹配多个catch语句,以捕捉不同的异常类型。这里必须注意的是多个catch语句的顺序,后面的catch语句中的参数类型不能是前面类型的子类,也不能和前面的参数类型相同。
如果catch语句的参数类型没有匹配try语句侦测到的异常类型,异常将在该语句的方法体内被抛出。如果调用该方法的方法没有相应的侦测和捕捉机制,那么异常继续被抛出,直到被捕捉,或者被抛出到应用程序的入口点Main函数,从而引起程序异常中断执行。这种异常抛出的机制对于在try语句之外出现的异常也同样适用——侦测到但没有捕捉到和没有侦测的效果一样!另外一点需要注意的是程序一旦发生异常,那么其后面的代码将不被执行!
但如果程序逻辑需要不管出现异常与否,都要执行某些任务紧急的代码(典型的如关闭打开的文件,网络端口等)怎么办呢?C#提供finally语句来解决这种情况。finally语句不能单独使用,它和try、catch有两种搭配情况: Try、catch、finally三者搭配构成try-catch-finally语句,在三者的搭配中,即使抛出了异常,finally语句也被执行; try语句还可以和finally语句搭配构成try-finally语句。try-finally语句侦测到异常后,只是简单地抛出,并没有捕捉。但在这种异常得到处理(被调用其方法的catch语句捕捉,或者到达Main函数入口点被执行环境中断)后,finally语句仍然执行。当然finally语句块只能有一个,这是不言自明的。
throw语句是用来抛出异常的,在代码设计中,遇到违背程序设计设定的条件,或异常的情况,我们常常要抛出特定的异常,这时候throw语句就很有用了,我们来看下面的例子:
using System;
public class MyException:ApplicationException {
public MyException (String message) : base (message) {}
public MyException (String message, Exception inner) : base(message,inner) {}
}
public class MyClass{
public static void ExMethod() {
throw new MyException(“Exception in ExMethod”); }
public static void MyMethod(){
try {ExMethod(); }
catch (Exception e) {
throw new MyException(“Exception in MyMethod”,e);}
}
}
public class Test {
public static void Main() {
try {
MyClass.MyMethod(); }
catch(Exception e) {
Console.WriteLine(e);
Console.WriteLine();
Console.WriteLine(e.GetBaseException());
}
}
}
程序输出:
MyException: Exception in MyMethod——> MyException: Exception in ExMethod
at MyClass.ExMethod()
at MyClass.MyMethod()
——End of inner exception stack trace——
at MyClass.MyMethod()
at Test.Main()
MyException: Exception in ExMethod
at MyClass.ExMethod()
at MyClass.MyMethod()
在上面的例子,我们设计了自己的异常类MyException,并在自己的设计类中简单地应用了这种异常类。测试程序中展示了它的处理,以及在多个方法调用中异常的追踪。其中e.GetBaseException()是调用System.Exception的GetBaseException()方法,获得引起异常e的最开始的那个异常,如果没有,将返回e自己。System.Exception的很多方法和属性为我们提供了很好的对异常的描述和追踪服务,它是我们应用异常、设计异常、认识异常的一个很好的起点。
Checked与Unchecked
对于因为整数类型参与算术操作和类型转换时产生的“溢出异常”——System.OverflowException,在某些算法来讲不算真正的“异常”,相反这种溢出常常为程序所用。C#通过引入checked和unchecked关键字来控制这种特殊情况的需求。它们都可以加于一个语句块前(如:checked{……}),或者一个算术表达式前(如:unchecked(x+y)),其中加checked标志的语句或表达式如果发生算术溢出,则抛出System.OverflowException类型的异常,而加unchecked标志的语句发生算术溢出时,则不抛出异常。下面是一个示例:
using System;
class Test{
static void Main() {
int num1=100000,num2=100000,
result=0;
checked{ try { result= num1 * num2;}
catch(System.Overflo2wException e){ Console.WriteLine(e); }
finally{ Console.WriteLine(result);}
}
unchecked{ try { result= num1 * num2;}
catch(System.OverflowException (e){ Console.WriteLine(e);}
finally{ Console.WriteLine(result);}
}
}
}
程序输出:
System.OverflowException: Arithmetic operation resulted in an overflow.
at Test.Main()
0
1410065408
可以看到同样的算术操作,用checked抛出了溢出异常,而unchecked只是将溢出的位丢弃而得到剩下的32位组成的十进制整数值。值得指出的是可以用“/checked”编译器选项指定整个文件的代码为checked语义,如果没有指定则默认为unchecked。如果同时在程序代码中指定checked或unchecked标志,又有了checked编译器选项,则除了标志为unchecked的代码外,其余的都有checked语义。