异常处理

来源:互联网 发布:apache 网络硬盘 编辑:程序博客网 时间:2024/06/10 02:53

保证程序的健壮性,有很多规范和要求,其中对于异常的有效处理应该是一种最有效的方法。有人将异常简单的认为是一种错误,显然是对异常的误解。Jeffrey Richter在《.Net 框架程序设计》中就提到这种误解。而他则认为异常并不必然代表错误。按我的理解,异常是一种可以预知类型的错误,然而其触发的时机却不可预知。

举例来说,我们在项目中要读取一个Xml配置文件,以获取相关配置信息。我们在部署时,是认定该配置文件必然是在指定目录中存在的。同时我们也很清楚,如果不存在该配置文件,在读取配置信息时,会发生未找到文件的错误。我们不能因为部署时该文件必然存在,就忽略这种可能发生的错误。此时就应该在程序设计时,考虑到可能会发生异常。因此必须捕获该异常,在异常发生时进行相应的处理。这就是说,异常发生的类型,我们是确切知道的,但什么时候会发生该异常呢?也许是在部署时,复制该配置文件发生了错误;也有可能是客户不小心删除了该配置文件。异常的发生时机,我们无法把握,因此处理的办法就是妥善地管理好异常。

以上述例子而言,代码可能会是这样:

 

try
{
   XmlDocument doc 
= new XmlDocument();
   doc.Load(xmlFileName);

   
//读取Xml配置文件;
}

catch (Exception ex)
{
   MessageBox.Show(ex.Message);    
}


也许上面的例子无法说明异常处理机制的优点。为什么呢?因为我们可以通过if语句来作出文件是否存在的判断。

if (File.Existed(xmlFileName)
{
   XmlDocument doc 
= new XmlDocument();
   doc.Load(xmlFileName);
   
return true;
}

else
{
   
return false;
}


确实这种思路是非常清楚的,然而我们试想一下,这样简单地返回bool值,是否是程序员需要的结果?读取配置文件操作失败,究竟是读取该文件错误,还是文件没有找到。简单的bool值,显然无法表示这个信息。又如果这个方法是在类库设计中,或者是一个组件方法。对于使用该组件的程序员而言,在某种情况下,有时候可能会成为某种困扰。

例如,有一个数据库访问组件,它提供插入一条记录的方法,然后返回操作成功或失败的标志。然后我们使用该组件,对数据库进行操作。当向该方法传递存储过程插入记录时,失败了。那么究竟是哪里发生了错误?是自己的代码错误,还是存储过程错误,或者是数据表不存在,甚至是组件本身提供的方法有误?从false值中,我们根本无法获得具体的信息,这给我们调试程序带来很大的困难。

不过上面的关于异常处理的例子还无法说明,异常的优越性。事实上,我们在捕获异常时,应尽量避免捕获System.Exception异常。在.Net中,该异常为所有异常类的基类。因此,在try程序段中,无论发生何种类型的错误,都会被Catch捕获到。那么返回的异常信息,可能并不是我们所需要的。

在.Net中,提供了用户自定义异常类的机制。在上面的例子中,我们可以定义FileErrorException类,它派生自System.Exception,调用基类的构造函数,然后再重写其Message字段。

 

public FileErrorException():base()
{}

public FileErrorException(string message):base(message)
{}

public FileErrorException(string message,Exception innerException):base(message,innerException)
{}

public override string Message
{
   
//显示异常信息;
}


那么捕获的时候应该是这样:

catch (FileErrorException ex)
{
    
//
}


在组件设计,我们通常采取的策略是,我们捕获异常,然后再抛出一个新的异常。这个异常可以是自定义的异常,也可以是系统内置的异常。抛出异常的目的,是使使用该组件的开发人员能够很好地获取异常发生的信息。.Net中,使用throw语句来抛出:throw new FileNotFoundException(“File not found!“);

.Net的异常机制中,除了try和catch外,还有finally程序块。通常情况下,会把资源释放的相关操作集中放在里面。例如文件操作:

FileStream fs = new FileStream(@"e: est.txt",FileMode.Open);
try
{
  //对文件流操作;

}

finally
{
  fs.Close();
}


.Net还提供了一种更简单的方法,就是using语句,它会自动地释放资源:

using (FileStream fs = new FileStream
(
@"e: est.txt",FileMode.Open))
{
    
//对文件流操作;
}


注意到,在使用using语句时,根本不需要再显示地关闭文件流,using语句会自动关闭文件流。

异常处理机制还有一个好处就是,当异常发生后,可以回滚到前面的操作,以恢复可能发生改变的数据。例如银行的转帐处理。从一个账户将钱转到另一个帐户,一旦在转账过程中发生异常,可能会出现钱已经转出,而另一个帐户还未收到的情况。此时,由于异常发生,操作已经无法继续,则必须恢复转出资金的帐户金额。

public bool TransformAccount(Account sender,Account receiver,double amount)
{
    
try
    
{
        sender.Fund 
-= amount;
        receiver.Fund 
+= amount;
    }

    
catch (SendFailureException ex)
    
{
         sender.Fund 
+= amount;
         
throw;
    }

}


注意在catch中,该异常是自己定义的异常。它定义了转帐过程发送数据失败的异常。在实际中,可能还会有其他异常,例如发送方帐户余额不够,或发送方和接受方为同一帐户等异常。异常不同,回滚的处理也不同。

以上只是简单地介绍了.Net的异常处理机制。浅尝而止,很多是自己都还不太明白的。总而言之,使用好异常,可以极大地保证程序的健壮性,提高程序的容错机制。然而异常不能滥用,毕竟对于异常的处理在一定程度下是会影响程序的性能的。这就有必要在设计时合理地权衡,把握使用异常的时机。

http://www.cnblogs.com/wayfarer/articles/35537.aspx

原创粉丝点击