原文载于: 正确使用异常处理可以让代码逻辑变得清晰,使程序的鲁棒性更好,并可以准确捕捉到一些细节的错误。那么问题来了,异常的使用场景是什么,实践中又有哪些常见问题,本文简单讨论一二。 何时用Exception?《The Pragmatic Programmer》第24节探讨了这个问题:When to Use Exceptions? 在程序中常常要检查各种可能存在的错误,但检查可能存在于比较深层的嵌套或多层函数调用之中,于是代码会变得非常难看。问题主要存在于两个方面: 如何将底层调用的错误处理传回顶层? 是否要对下层调用的返回值进行检查并处理? 笔者在code review中见过不少蹩脚的Exception使用,根本原因在于没有理解上面两个问题。Exception使用不当不仅不能使代码变得简洁,反而会隐藏底层的问题,并且掩盖了主逻辑想要做什么。笔者将这种异常使用方法叫做“满篇的try-catch-check-null”(切克闹切克闹) :-),生怕你看懂代码真正想要做什么。 try{ var book = GetBook(id); if (book == null) { Logger.Error("Book is null"); } ...}catch (Exception ex){ Logger.Error(ex.Message); throw;} 这段“切克闹”其实有用的就一行代码 try{ if (condition) { throw new CustomException(); } ...}catch (Exception ex){ Logger.Error(ex.Message);} 这样的用法自产自销除了增加开销,与goto有什么区别?把catch中的处理异常语句直接放在if字段中即可。 再引用《The Pragmatic Programmer》中的例子: retcode = OK;if (socket.read(name) != OK){ retcode = BAD_READ;}else{ processName(name); if (socket.read(address) != OK) { retcode = BAD_READ; } else { processAddress(address); if (socket.read(telNo) != OK) { retcode = BAD_READ; } else { ... } }}return retcode; 在主流程中判断了每个中间处理函数可能出现的错误,导致if嵌套很多,喧宾夺主,主逻辑不清晰。如果使用exception改进,代码清晰不少: retcode = OK;try{ socket.read(name); processName(name); socket.read(address); processAddress(address); socket.read(telNo); ...}catch (IOException e){ retcode = BAD_READ; Logger.Error("Error reading individual: " + e.Message);}return retcode;} 并且这只是个非常简单的示例,实践中对于调用层数很深的代码,只在顶层捕捉Exception可以大大简化中间每层的检查逻辑,同时可以轻松地将底层的错误直接传递回顶层。 但Exception其实是一种类似goto的语句,如果不当使用,反而会降低程序的可维护性和效率。因此,作者给出建议: Tips: Use Exceptions for Exceptional Problems 而对于什么是 用Exception时不要做什么这里的建议就与C#语言相关了,本段摘自:Things to Avoid When Throwing Exceptions The following list identifies practices to avoid when throwing exceptions: - Exceptions should not be used to change the flow of a program as part of ordinary execution. Exceptions should only be used to report and handle error conditions. - Exceptions should not be returned as a return value or parameter instead of being thrown. - Do not throw System.Exception, System.SystemException, System.NullReferenceException, or System.IndexOutOfRangeException intentionally from your own source code. - Do not create exceptions that can be thrown in debug mode but not release mode. To identify run-time errors during the development phase, use Debug Assert instead. 这里特别说下第三条:throw new Exception("xxx")。在上面Programming Guide上只说不要这么做,却没解释是为什么。原因在这个帖子中有讨论:# Why are we not to throw these exceptions? 总结起来,如果直接new Exception()的话,第一Exception太宽泛,相当于啥也没说,Exception的定义讲究尽量详细和具体,以便给调用者提供明确的错误信息。第二外面的try block中就必须catch (Exception),即catch所有的异常,这也是不好的做法。同理,MSDN上也不建议抛ApplicationException。其实与其这样告诉大家Guideline,还不如直接将Exception声明为abstract,这样就无法创建实例了,也就从根本上避免了这种做法。个人猜想可能因为兼容性的问题才没有改成这样。 至于NullReferenceException和IndexOutOfRangeException,它们是由系统在运行时自动抛出的异常,属于“reserved exception”,手动抛这样的异常出去就很不合理。 |
|
来自: abcen > 《Exception》