异常简介
不可靠的程序含有很多“臭虫”(也叫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。