异常简介
索引越界、文件I/O出错、堆栈溢出、除零运算问题、数据库无法使用等一个个问题,对于我们来说如何保证系统容错和程序规范,异常机制是不可或缺的重要因素和手段。良好的系统设计必定有良好的异常处理机制来保证程序的健壮性和容错机制。
不可靠的程序含有很多“臭虫”(也叫Bug),含有臭虫的代码我们称之为有”臭味”(BadShell)。软件领域的Bug是无处不在的,所以,不要轻言自己的程序已经完美了。其实,只是你现在还没发现Bug,或者说还没有能力来发现Bug而已,当然,完全没有Bug的程序是不存在的,我们只能尽量去发现。正如软件测试的目的一样:尽可多的去发现错误。
那么,我们回想以前自己做的程序,是否在编程的时候针对一些细节问题没做处理呢?比如:除零问题、文件I/O问题等。错误按照发生机理一般可以分为两类:
一.语法错误
类似于我们语文中的写了一篇文章不加标点符号一样,导致会出现多种读法。在C#程序中,语句的开始结束有强制的规定,不能多或少一些符号,毕竟计算机还是基于逻辑判断的机器。常见的语法错误如下:
1) 语句结束少了分号;
2) 类型匹配错误。
3) 变量书写错误。
4) 空值错误。“空对象或空引用”的问题。
5) 相等值比较。
6) 数组越界。
7) If else 或switch case 匹配问题。
8) 随便使用析构函数。
二.逻辑错误
1) 语法错误导致语法逻辑错误。
2) 处理逻辑本身的错误。例如:求圆周长:使用公式2*PI*R*R,避免此种错误,就需要一本小学数学书籍做参考了。:)
假如为银行做ATM项目(自动取款机),个人感觉利息的计算精确到小数点后两位就可以了。假如说你省略带的利息位0.005(四舍五入到0.01)计算,10000000次交易就是5000元。对于客户或银行都可能造成莫大的损失。
或者在ATM项目中没有考虑取款额范围的问题,假如有客户一次取款1000000,那么ATM机可能会忙的发热烧掉哦。即使没烧掉,也可能没这么多钱让你取。
|
深入理解异常机制,需要认真研究try/catch/finally块应用。在C#中针对程序中可能出现的各种异常,主要有如下几种。
1. 一个try块后跟一个或多个catch块。
Try
{
目标代码块
}
catch(异常类名1 异常变量名2)
{
异常处理代码1
}
catch(异常类名2 异常变量名2)
{
异常处理代码2
}
2. 一个try块后跟一个或多个catch块
Try
{
目标代码块
}
catch(异常类名1 异常变量名2)
{
异常处理代码1
}
catch(异常类名2 异常变量名2)
{
异常处理代码2
}
finally
{//无论是否执行,代码都会执行
}
|
项目背景:排除除零错和其他常规错误。
步骤1:考虑有多少种异常
步骤2:编码、采用多个catch块
usingSystem;
classExceptionDemo
{
static void Main(string[]args)
{
try
{
intnum1,num2,result;
Console.WriteLine("本程序实现除法运算:");
Console.Write("请输入被除数:");
num1=Int32.Parse(Console.ReadLine());
Console.Write("请输入除数:");
num2=Int32.Parse(Console.ReadLine());
result=num1/num2;
Console.WriteLine("{0}/{1}={2}",num1,num2,result);
}/*catch(DivideByZeroExceptiona) //e:这是一个DivideByZeroException异常类的对象;
{
Console.WriteLine("-----------------------");
Console.WriteLine("{0}",a);
Console.WriteLine("异常信息:{0}",a.Message);
Console.WriteLine("引发异常的来源:{0}",a.Source);
}catch(OverflowException)//在选中的上下文中所进行的算术运算、类型转换或转换操作导致溢出时引发的异常
{
Console.WriteLine("您输入的数字太大了,已经超过系统允许的范围!");
}*/
catch(FormatException)//当参数格式不符合调用的方法的参数规范时引发的异常。
{
Console.WriteLine("您输入的格式不对");
}
catch(Exception)
{
Console.WriteLine("我的程序没有错误,永远对!!!打肿脸充张洋");
} //首先catch子类(范围比较小的),然后catch超类(范围比较大的异常类)
finally
{
Console.WriteLine("最后不管有没有问题都执行!!!");
//垃圾回收不好控制:比如说数据库连接;最后需要关闭数据库连接.
//里面还可以写try-catch-finally
}
}
}
步骤3:编译csc ExceptionDemo.cs
步骤4:运行ExceptionDemo
步骤5:观察效果,并思考是否可以进一步改进代码
|
异常类的层次结构图如下,当然不能在次将所有异常类都一一列举。主要让大家明了系统异常类和自定义异常类。`
常见系统异常类说明如下:
常见系统异常类说明如下:
系统异常类 |
说 明 |
IndexOutOfRangeException |
数组索引异常 |
NullReferenceException |
空引用异常(对象为null,而直接使用) |
DivideByZeroException |
除数为0时异常 |
IOException |
文件读写IO操作异常 |
DataException |
数据库访问操作异常 |
ApplicationException |
非致命应用程序引发异常 |
ArrayTypeMismatchException |
数组类型不正确的引发异常 |
OverflowException |
算术运算、类型转换导致的溢出异常 |
InvalidCastException |
无效类型转换引发异常 |
OutOfMemoryException |
没有足够内存继续执行程序引发的异常 |
了解一个异常类的体系,有必要认真学习System.Exception这个基类,其常用属性如下:
|
名称 |
说明 |
Data |
获取一个提供用户定义的其他异常信息的键/值对的集合。 |
|
HelpLink |
获取或设置指向此异常所关联帮助文件的链接。 |
|
HResult |
获取或设置 HRESULT,它是分配给特定异常的编码数值。 |
|
InnerException |
获取导致当前异常的 Exception 实例。 |
|
Message |
获取描述当前异常的消息。 |
|
Source |
获取或设置导致错误的应用程序或对象的名称。 |
|
StackTrace |
获取当前异常发生时调用堆栈上的帧的字符串表示形式。 |
|
TargetSite |
获取引发当前异常的方法。 |
|
在C#中,有如下两种异常引发方式。
n 主动引发异常:使用throw语句来立即、无条件地引发异常。控制永远不会到达紧跟在throw后面的语句。对程序来说,主动地检查到了错误或者意外事件,因此引发了一个异常来通知用户或者调用代码。
n 系统引发异常:在执行C#语句和表达式的过程中,有时会出现一些例外情况,使某些操作无法正常完成,此时就会引发一个异常。对程序来说,这种异常是被动地发生的。例如,整数除法运算中,如果分母为零,则会引发System.DivideByZeroException,会出现这种异常是因为进行除法之前,程序没有主动地检查除法是否为零。一般地,系统引发的异常都是.Net框架类库中定义的公共异常类所表示的异常。
Throw语句格式:
Throw[异常表达式]
带表达式的throw语句引发一个异常,此异常的值就是通过计算该表达式而产生的值。计算该表达式所得的值是System.Exception或从System.Exception派生的类的对象。如果表达式的计算产生null,则引发System.NullReferenceException。
不带表达式的throw语句只能用在catch块中,此种情况下,该语句重新引发当前正由该catch块处理的那个异常。
由于throw语句无条件地控制到别处,因此永远无法到达throw语句的结束点。
案例:
// Rectangle.cs
// throw示例
using System;
public class Rectangle
{
protected int width;
protected int height;
public int Width
{
get
{
return width;
}
set
{
if (value > 0)
width = value;
else
throw new Exception("Width的值不能为负数。");
}
}
public int Height
{
get
{
return height;
}
set
{
if (value > 0)
height = value;
else
throw newException("Height的值不能为负数。");
}
}
public Rectangle()
{
}
public Rectangle(int cx, int cy)
{
Width = cx;
Height = cy;
}
public static void Main()
{
Rectangle rect = new Rectangle();
rect.Width = -1;
}
}
运行上述程序将会引发异常,因为当给Rectangle类的对象的Width属性赋负值时,将调用throw语句。
|
有时我们可能会碰到系统异常类无法提供合适异常类型的情况,此时我们就需要自定义异常类。格式:
classMyException:ApplicationException
{
Public MyException():base(“自定义异常处理消息”)
{}
}
为什么可以调用基类的带字符串的构造函数,System.Exception的构造函数格式会看出端倪。
PublicException(string message);
PublicException(string message,Exception innerException);
可以使用throw语句来抛出自定义的异常,代码如下:
//MyException.cs
// 自定义异常类示例
usingSystem;
classMyException : ApplicationException
{
public MyException()
: base("自定义异常信息。")
{
}
}
classTest
{
public static void Main()
{
try
{
throw new MyException();
}
catch (MyException ex)
{
Console.WriteLine(ex.Message);
}
}
}
|
选择题:
1. 异常处理块可以使用的关键字下面哪一个不正确()
A try Bcatch
C finally D final
2.下面哪一个不是在程序中发生错误的类型()
A 语法错误 B运行时错误
C 空值错误 D 逻辑错误
3.没有任何表达式的throw 语句紧被用在以下哪个块中()
A finally B catch
C throw D try
4.所有的异常都派生自()类
A System.Exception B System.ApplicationException
C System.SystemException D System.DataException
|
在本章中,我们主要学习了:
u 异常是什么
u 如何使用异常
u 异常的类型和层次结构
u 开发自己的自定义异常类
|
英文
全文
中文
Exception 异常
Range 范围
Null 空的
Memory 内存
Try 尝试
Catch 捕获
Finally 最终
Handle 处理
Throw 抛出
Average 平均数
Found 发现, 找到
IOException 文件读写等IO操作异常
DataException 进行数据库访问等操作发生错误时引发的异常
ApplicationException 发生非致命错误时引发的异常
|
1.编写一个程序,要求用户输入手机号,手机号必须符合规范。编写一个手机号格式不符合规范的自定义异常继承自Application,如果手机号不符合规范则抛出异常错误。
手机规范为:只能是13位数字,不能是其他任何字符。第一必须是1, 第二位必须是3或5。