C#锐利体验 第十七讲 异常处理

C#锐利体验

南京邮电学院 李建忠( lijianzhong@263.net.cn

第十七讲 异常处理
异常处理
???????结构化异常处理是现代分布式环境下组件设计的一个必要的环节,.NET通用语言运行时从底层构造给予异常处理以坚实的支持。在C#中,异常对象被设计为封装了各种异常信息的类(System.Exception及其继承子类,和接口类似,它被推荐命名为加后缀“Exception”的方式,但这并非必须),“try-catch-finally”语句和异常对象一起为C#组件设计提供从异常侦测,异常捕捉和处理等一揽子服务。先来看一个典型的例子:
using System;
class Test
{
????public static void Main()
????{
????????int x = 0;
????????try
????????{
????????????int y = 10/x;//除零异常
????????????Console.WriteLine(“try End!”);
????????}
????????catch (DivideByZeroException e)
????????{
????????????Console.WriteLine("DivideByZeroException --> "+e);
????????}
????????catch (Exception e)
????????{
????????????Console.WriteLine("General Exception --> "+e);
????????}
????}???
}
示例将产生下面的输出:
DivideByZeroException --> System.DivideByZeroException: Attempted to divide by zero.
???at Test.Main()
我们在上面的try语句里特意进行除零操作从而产生“除零异常”,在第一个catch语句里我们便捕捉到了“除零异常”——DivideByZeroException,这是catch内指定的类型使然。从结果也可以看出,我们的第二个catch语句并没有捕捉到任何其他异常。这个示例虽然简短,却为我们揭示了异常处理的基本模型。
???????首先我们将可能出现异常的语句放在try语句块内,这样的代码中出现的异常才可能被侦测到并传递给catch语句。catch语句可以带参数,也可以不带参数。catch语句带的参数类型只能为System.Exception及其继承子类。带参数的catch语句将只捕捉参数指定类型的异常,对于其他的异常不予处理。不带参数的catch语句相当于参数为System.Exception类型的异常(即所有的异常),只不过不能获取异常变量(如上面示例中的e)。一个try语句块后面可以匹配多个catch语句,以捕捉不同的异常类型。这里必须注意的是多个catch语句的顺序,后面的catch语句中的参数类型不能是前面类型的子类,也不能和前面的参数类型相同,无参数相当于System.Exception类型。这个规定在针对特定类型的异常处理的意义下是很自然的——如果AException类型是BException类型的基类,那么如果出现BException类型的异常,catch(AException e)也能够捕捉得到,这样下面将捕捉不到BException类型的异常,catch(BException e)也将失去意义。C#编译器将对这种情况报错。
???????那么如果我们的catch语句的参数类型并没有匹配try语句侦测到的异常类型会怎么样呢?当这种情况发生时,异常将在该语句的方法体内被抛出。如果调用该方法的方法没有相应的侦测和捕捉机制,那么异常继续被抛出,直到被捕捉,或者被抛出到应用程序的入口点Main函数从而引起程序异常中断执行。这种异常抛出的机制对于在try语句之外出现的异常也同样适用——侦测到但没有捕捉到和没有侦测的效果一样!
???????另外一点需要注意的是程序一旦发生异常,那么其后面的代码将不被执行!上面示例中的Console.WriteLine(“try End!”)就由于前面的代码发生“除零异常”而没有执行。这一点也决定了我们的代码只能被侦测到一次异常,最多也只能捕捉一次异常!
但如果我们程序逻辑需要我们不管出现异常与否,都要执行某些任务紧急的代码(典型的如关闭打开的文件,网络端口等)怎么办呢?C#提供finally语句来解决这种情况。finally语句不能单独使用,它和try,catch有两种搭配情况。try,catch,finally三者搭配构成try-catch-finally语句。看下面的例子:
using System;
public class Test
{
????public static void Main ()
????{
????????try
????????{
????????????Console.WriteLine("try ...");
????????????throw new NullReferenceException();//抛出异常
????????}
????????catch(NullReferenceException e)
????????{
????????????Console.WriteLine("NullReferenceException : {0}",e);
????????}
????????catch(Exception e)
????????{
????????????Console.WriteLine("Exception : {0}",e);
????????}
????????finally
????????{
????????????Console.WriteLine("finally ...");
????????}
????}
}
程序输出:
try ...
NullReferenceException : System.NullReferenceException: Object reference not set to an instance of an object.
???at Test.Main()
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.OverflowException 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语义。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
// DivideByZeroException.java public class DivideByZeroException extends ArithmeticException { public DivideByZeroException() { super("Attempted to divide by zero"); } } 这段代码定义了一个自定义异常类DivideByZeroException,它继承了Java内置的ArithmeticException类。在构造函数中,它调用了父类的构造函数,并传递了一个字符串参数,用于描述异常情况。 // DivideByZeroExceptionTester.java public class DivideByZeroExceptionTester { private static int quotient(int numerator, int denominator) throws DivideByZeroException { if (denominator == 0) { throw new DivideByZeroException(); } return(numerator / denominator); } public static void main(String args[]) { int number1 = 0, number2 = 0, result = 0; try { System.out.println("Enter the first number:"); number1 = Integer.valueOf(Keyboard.getString()).intValue(); System.out.println("Enter the second number:"); number2 = Integer.valueOf(Keyboard.getString()).intValue(); result = quotient(number1, number2); } catch (NumberFormatException e) { System.out.println("Invalid integer entered!"); System.exit(-1); } catch (DivideByZeroException e) { System.out.println(e.toString()); System.exit(-1); } System.out.println(number1 + " / " + number2 + " = " + result); } } 这段代码包含了一个静态方法quotient,用于计算两个整数的商。如果除数为0,它将抛出自定义的DivideByZeroException异常。main方法中,程序通过调用quotient方法来计算两个整数的商,并在捕获异常后输出相应的提示信息。如果用户输入的不是整数,也会抛出NumberFormatException异常。 具体而言,这段代码: 1. 定义了一个名为DivideByZeroExceptionTester的类。 2. 在类中定义了一个静态方法quotient,该方法接受两个整数作为参数,并返回它们的商。如果除数为0,则抛出自定义的DivideByZeroException异常。 3. 在main方法中,程序提示用户输入两个整数,并通过调用quotient方法来计算它们的商。如果用户输入的不是整数,会抛出NumberFormatException异常。如果除数为0,会抛出自定义的DivideByZeroException异常。 4. 在捕获异常后,程序输出相应的提示信息,并使用System.exit(-1)退出程序。如果没有发生异常,则输出计算结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值