极地银狐.NET

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  144 随笔 :: 11 文章 :: 1249 评论 :: 9 引用

[原文: Best Practices: Exception Management]

[出处: http://www.dotnetjunkies.com/]

[作者: Anand Kumar]

[翻译:极地银狐.NET]

 

介绍

在任何应用系统开发中,异常管理对开发人员来说都是一个关键领域。作为一个开发人员,你应该采用一种合适的、能让你编写健壮且高质量代码的异常管理策略。如果使用得当,异常管理将是一个很强大的概念而且使你的开发工作变的很容易。然而,一个不合适的异常处理将会使你的应用系统性能降低。

 

在你钻研这些之前,要先搞清楚异常管理是什么,这点很重要。一般来说异常就表示“打破系统预定的假设”。“异常”和“错误”是不一样的,为了解释清楚,我们来看两个例子:

示例一

       我们假设你试图向一个文件写入数据,而你的应用程序也认为这个文件在正确的路径上。如果不在,就会抛出一个异常。而话说回来,如果你的职责是跟踪此文件,那么代码里还有(找不到文件的)异常那将是一个不好的编码习惯,这种情况应该被校验代码处理(而不是异常)。

示例二

       再让我们假设一个一般的ASP.NET程序中,你正试图更新数据库中所有必需的字段。你的应用程序认为此数据库连接可用,假设实际上这个连接是不可用的……抛出异常是一个解决方案。而我们又把话说回来,如果更新数据库的必填字段时出现有几个值为空的字段,那抛出异常就没有必要了,这些处理应该由校验代码完成。

如何处理异常

       作为一个开发人员,你应该感受到通过try, catch, finally块来构建一个结构化异常处理机制的优点。.NET框架提供了一大堆异常处理层次来处理不同种类的异常。所有的异常都继承自Exception(基类)。你可以通过继承来实现自定义错误处理以扩展异常处理机制。不幸的是,很多开发人员都误用了这种架构能力。一个随时要记着的事是当一个异常发生在运行时时(这个架构)应该如何运作?一般有以下三种情况:

  1. 忽略异常,让它在调用栈里上升而被其它的catch块捕获。
  2. 捕获异常,同时为你的应用程序执行必要的动作,如果你不想再次在异常中抛出异常的话。
  3. 捕获异常,并用其它异常覆盖它。这样和你的应用程序有更密切的关系。异常覆盖是为了避免打破(架构中的)抽象层次。你可以通过你抛出的异常的InnerException属性指定原异常是什么,这样就可以把你现有的异常用一个新的异常来覆盖了(更与你系统有关的)。为了了解异常覆盖,让我们来看一个能引起IOException异常的方法,你可以在应用级别使用LoadingException 或 FailtoLoadInfoException来覆盖原有的IOException异常,这样比把底层的IOException给用户看到要来的好些。

 

一个应用程序的异常处理框架应该有以下几种(要求):

    • 探测异常;
    • 执行代码清除;
    • 内部异常覆盖;
    • 内部异常替换;
    • 记录并报告错误信息;
    • 建立能被外部监视的事件以帮助系统操作;

 

在开始你应该建立一个一致的,健壮的异常管理架构,在你所有的系统中应该很好的封装并抽象其记录和报告等的细节。

好的习惯

       以下列出一些不错的提示/建议供你在(设计)异常处理(时)参考:

  • 抛出异常要不小的代价。所以,你应该尽可能地在“异常情况”下进行异常处理,不要去控制正规逻辑流程。比如,以下代码:
void EmpExits(string EmpId)
{
    
// search for employee
    if(dr.Read(EmpId) == 0// no record found
    {
        
throw(new Exception("Emp Not found"));
    }
}

应该用以下代码:
bool EmpExits(string EmpId)
{
    
// search for employee
    if(dr.Read(EmpId) == 0// no record found
    {
        
return false;
    }
}

 

  • 避免在循环中捕获异常,如果实在是要捕获,那把整个循环都放在try/catch块里。
  • 采用标准try/catch/finally异常处理方式进行处理,这在托管代码里是被推荐的。最后的Finally块确保异常事件中的资源都被释放掉了。
    比如:
SqlConnection conn = new SqlConnection("");
try
{
    conn.Open();
    
// some operation
    
// some additional operations
}
catch(Exception ex)
{
    
// handle the exception
}
finally
{
    
if (conn != null && conn.State == ConnectionState.Open)
        conn.Close(); 
// closing the connection
}

 

  • 尽可能的用校验代码而避免使用异常。如果你知道一个可避免的条件可能会出现,那就让它避免。比如,在执行任何操作以前,检查空值(VB里是Nothing),这样可以避免使用异常以及性能问题。
    以下代码:
double result = 0;
try
{
    result 
= firstVal/secondVal;
}
catch(System.Exception e)
{
    
// handling the zero divided exception
}

应该替换成:
double result = 0;
if(secondVal != null && secondVal > 0)
{
    result 
= firstVal/secondVal;
}
else
{
    result 
= System.Double.NaN;
}

 

  • 不要为没有必要的情况(原文:reasons)抛出异常。再次抛出异常的开销和实例化一个新异常的开销一样的大,同时再次抛出异常使程序调试工作增加难度。比如:
try
{
    
// Perform some operations ,in case of  throw an exception…
}
catch (Exception e)
{
    
// Try to handle the exception with e
    throw;
}

 

  • 推荐的处理不同的错误的不同的方法是实现一系列的catch块,这看起来好像没有什么,但可以让你的异常处理从特殊走向普通。比如捕获一个和文件有关的异常明显要比捕获一个FileNotFoundException, DirectoryNotFoundException, SecurityException, IOException, UnauthorizedAccessException甚至最后的基类Exception,好的多。
  • ADO.NET 的错误应该通过 SqlException 或 OleDbException来处理。
  • 使用ConnectionState属性来检查连接是否可用要比异常处理好的多。
  • 要常使用Try/Finally,Finally提供了关闭连接的机会。Using语句可以达到同样的效果。
  •         用指定的处理程序来处理异常。在一些情况下如果你知道一些可能的异常那就用相应的异常处理类,比如:

 

try
{
    
}
catch(SqlException sqlexp) // specific exception handler
{
    
}
catch(Exception ex) // Generic exception handler
{

}

 

  • 你的异常处理架构应该可以探测异常并在内部将其覆盖,(或是)使用其它异常将其替换,或是为监视系统而记录和报告这些信息。
  • 推荐大家使用 Microsoft's patterns & practices 团队 提供的Exception Management Application Block。这是一个简单且可扩展的框架用于记录异常信息到事件文件中,你可以自定义它,把日志记录到其它数据源中同时不影响你系统的代码。Exception Management Application Block都是一些由patterns & practices团队开发的很好的代码,并且已经彻底地被Microsoft labs给测试过了。
posted on 2006-04-26 01:39 极地银狐.NET 阅读(1887) 评论(8)  编辑 收藏 所属分类: 开发人生代码天下

评论

#1楼  2006-04-26 10:16 Tony.Gong      
不要为没有必要的情况(原文:reasons)抛出异常。再次抛出异常的开销和实例化一个新异常的开销一样的大,同时再次抛出异常使程序调试工作增加难度。

这句话是什么意思?
  回复  引用  查看    

#2楼 [楼主] 2006-04-26 10:22 极地银狐.NET      
@Tony.Gong:
原文是这样的:
Do not rethrow exception for unnecessary reasons. The cost of using throw to rethrow an existing exception is the same as creating a new exception. Rethrowing an exception also makes it very difficult to debug the code.
这里rethrow 我只好翻译成"再次抛出"了.
给点意见?呵呵.
  回复  引用  查看    

#3楼  2006-04-26 13:28 基点项目师      
个人觉得,如果需要再次抛出异常的地方分为两种情况
1.对原异常不进行任何包装,而直接使用throw往上层抛出,那么个人觉得这里就不需要对这个异常进行处理,而直接在需要捕捉的那一层进行捕捉。
2.需要对原始异常进行再包装,那么可以把原始异常记入新异常的InnerException中,这样便也不存在不好跟踪的麻烦了。
  回复  引用  查看    

#4楼  2006-04-26 19:37 2 [未注册用户]
我真的没有天份、安静的没这么快。人笨又能怎样,不就是一个没有用的人吗?不就是社会上的废人吗?不过这样的人活着有意义吗?
我真的想不通,为什么别人都可以聪明
而我不行,我真的好伤心、好想死。也就是说我真的好想解脱,活着,是我最大的痛苦
我有时真的好恨老天,恨他给予我的一切!!!!!!!真的
可是又能怎样?

  回复  引用    

#5楼  2006-05-08 17:02 蛙蛙池塘      
好,应该好好学习学习。
  回复  引用  查看    

#6楼  2006-05-29 10:52 焰凌      
除了顶,没别的了
  回复  引用  查看    

#7楼  2006-09-23 19:34 壮志      
不知道什么时候使用异常,什么时候返回错误码
  回复  引用  查看    

博客站点:http://xghksix.com
博客站点:http://www.xghksix.com

  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-04-26 10:18 编辑过


相关链接: