断言与异常(Assertion Vs Exception)
在日常编程实践中,断言与异常的界限不是很明显,这也使得它们常常没有被正确的使用。我也在不断的与这个模糊的怪兽搏斗,仅写此文和大家分享一下我的个人看法。我想我们还可以从很多角度来区别断言和异常的使用场景,欢迎大家的意见和建议。
异常的使用场景:用于捕获外部的可能错误
断言的使用场景:用于捕获内部的不可能错误
我们可以先仔细分析一下我们在.net中已经存在的异常。
System.IO.FileLoadException
SqlException
IOException
ServerException
首先,我们先不将它们看成异常,因为我们现在还没有在异常和断言之间划清界限,我们先将它们看成错误。
当我们在编码的第一现场考虑到可能会出现文件加载的错误或者服务器错误后,我们的第一直觉是这不是我们代码的问题,这是我们代码之外的问题。
例如下面这段代码
public void WriteSnapShot(string fileName, IEnumerable<DbItem> items)
{
string format = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}";
using (FileStream fs = new FileStream(fileName, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Unicode))
{
...
foreach (var item in items)
{
sw.WriteLine(string.Format(format, new object[]{
item.dealMan,
item.version,
item.priority,
item.bugStatus,
item.bugNum,
item.description}));
}
sw.Flush();
}
}
}
上面的代码在写入文件,很显然会导致IOException。稍微有经验的程序员都会考虑到IO上可能出问题,那我们应该如何处理这个问题呢?在这个上下文中,我们别无它法,只能让这个错误继续往上抛,通知上面一层的调用者,有一个错误发生了,至于上一层调用者会如何处理,不是这个函数要考虑的问题。但在这个函数中,要记得一点,将当前函数中所占用的资源释放了。因此,当我们不能控制的外部错误出现时,我们可以将其作为异常往上抛,这时,我们该使用异常。
现在再来看看断言,我们还是以下面的一段代码为例子。
public Entities.SimpleBugInfo GetSimpleBugInfo(string bugNum)
{ var selector = DependencyFactory.Resolve<ISelector>(); var list = selector.Return<Entities.SimpleBugInfo>(
reader => new Entities.SimpleBugInfo
{
bugNum = reader["bugNum"].ToString(),
dealMan = reader["dealMan"].ToString(),
description = reader["description"].ToString(),
size = Convert.ToInt32(reader["size"]),
fired = Convert.ToInt32(reader["fired"]),
},
"select * from bugInfo",
new WhereClause(bugNum, "bugNum")); Trace.Assert(list != null); if (list.Count == )
return null;
else
return list[]; }
当我贴出这段代码时,心情有些坎坷,因为我本人在这里也纠结了很久,这也是我一直没有将断言和异常划清界线的原因之一。
首先我们来回顾一下之前定义的断言使用场景:内部不可能发生的错误。
selector.Return这段代码是不是内部代码?如果我们能够修改Return中的代码,说明它是内部代码;反之,说明它是外部代码。对于内部代码,我们可以用断言来保护其逻辑的不变性,当断言被触发时,我们就可以确信是内部代码的错误,我们应该立即修复。
再纠结一下,假设Return是外部代码,我们没有办法去修改它。那么上面的代码可以有两种写法(如果你有更多的想法,请赐教)。
第一种,直接抛出异常。
If(list == null)
{
throw new NullReferenceException();
}
第二种,调整代码。
if(list == null || list.Count == )
{
return null;
}
else
{
return list[];
}
当然,还有一种就是什么也不做,让代码执行下去直至系统为你抛出空引用错误。但这种做法违背了防卸性编程的原则,我们总是应行尽早或离错误的发生地最近的地方处理错误,避免错误数据流向系统的其它地方,产生更加严重的错误。
总结
对异常或断言的使用取决于你要防卸的是一个内部错误还是外部错误以及你认为它是一个内部错误或外部错误。如果你决定防卸一个内部错误,那请果断使用断言,反之,请使用异常。
参见:
.net 的异常继承树(http://msdn.microsoft.com/en-us/library/z4c5tckx(v=vs.110).aspx)
原代码来至于我的TeamView开源项目(http://teamview.codeplex.com)
巜从小工到大师》
断言与异常(Assertion Vs Exception)的更多相关文章
- Python 断言和异常
Python 断言和异常 Python断言 断言是一种理智检查,当程序的测试完成,可以将其打开或关闭.断言的最简单方法就是把它比作raise-if语句(或更加准确,raise-if-not声明).一个 ...
- 用Assert(断言)封装异常,让代码更优雅(附项目源码)
有关Assert断言大家并不陌生,我们在做单元测试的时候,看业务事务复合预期,我们可以通过断言来校验,断言常用的方法如下: public class Assert { /** * 结果 = 预期 则正 ...
- NET环境下的未处理异常(unhandled exception)的解决方案
NET环境下的未处理异常(unhandled exception )的解决方案 .Net 框架提供了非常强大的异常处理机制,同时对一些非托管代码很难控制的系统问题比如指针越界,内存泄漏等提供了很好的解 ...
- java异常—检查异常(checked exception)和未检查异常(unchecked exception)
网易面试要我画异常的结构图,什么是检查异常,什么是非检查异常,我当时的表情是这样的,.我看过,忘了.没办法,继续看,写博客掌握. 先来看看异常的结构图,建议你结合JDK一起看. 可以看出异常的家族势力 ...
- 检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)
这两个概念看了忘,碰着了又看,老是傻傻的分不清楚,今天把心得结合从网上搜的资料简单整理一下,希望帮自己明确区分开这两个概念,并牢牢的记住 1.检查型异常(Checked Exception) 个人理解 ...
- 2019-2-21.NET中异常类(Exception)
.NET中异常类(Exception) 异常:程序在运行期间发生的错误.异常对象就是封装这些错误的对象. try{}catch{}是非常重要的,捕获try程序块中所有发生的异常,如果没有捕获异常的话, ...
- 应用程序发生异常 unknown software exception (0xc00000fd)... - 栈溢出(Stack overflow)
今天在写程序的时候,弹出这样的提示对话框: 应用程序发生异常 unknown software exception (0xc00000fd): 相关代码是这样,在一个函数中读取一个csv文件,先根据这 ...
- java 异常和异常处理Exception
Java Exception: 1.Error 2.Runtime Exception 运行时异常3.Exception 4.throw 用户自定义异常 异常类分两大类型:Error类代表了编译和系统 ...
- Java 异常(Java Exception)
Java异常 异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类 ...
随机推荐
- 生成lua的静态库.动态库.lua.exe和luac.exe
前些日子准备学习下关于lua coroutine更为强大的功能,然而发现根据lua 5.1.4版本来运行一段代码的话也会导致 "lua: attempt to yield across me ...
- iOS-UI-UI控件概述
以下列举一些在开发中可能用得上的UI控件: IBAction和IBOutlet,UIView 1 @interface ViewController : UIViewController 2 3 @p ...
- php相对于java、js几点不太一样的地方
1.PHP 变量作用域 在 PHP 中,可以在脚本的任意位置对变量进行声明. 变量的作用域指的是变量能够被引用/使用的那部分脚本. PHP 有三种不同的变量作用域: local(局部) global( ...
- jquery 拖拽,框选的一点积累
拖拽draggable,框选 selectable,按ctrl多选,临近辅助对齐,从工具栏拖工具 等,和jqueryui的selectable不同,是在一个父div里框选子div(类似框选文件),一 ...
- Android 圆形头像 自己动手
圆形头像DIY 现在大部分app使用的都是圆形头像,网上开源的也很多,但是有没有考虑过DIY圆形头像呢?下面就自己实现一个,先看下demo展示 第一步:原理解释(图片很丑,原理很真) 1.画外框圆形, ...
- 页面loading效果
当网页太大,打开太慢的时候,为了增加良好的用户体验(不让用户眼巴巴的等,心中暗骂c,这么慢),我们需要加一个等待动画. 只需把以下代码加入页面中即可,图片可以根据自己的需求更换,更换图片之后需要改变l ...
- MySQL学习笔记二:权限管理
1. 创建和删除用户,mysql中的用户是由用户名和主机名来确定的 create user "user_name@host_name" identified by passwd; ...
- MongoDB一键式安装工具
一. 需求 不知道有多少人会像我一样,会把MongoDB用在客户端应用上,不过我感觉应该非常少.于是,我就希望能在客户机器上尽可能简单方便的安装MongoDB.所以以下是我的一些取舍: 1. 我只使用 ...
- Percona博客学习总结
1. Upgrading to MySQL 5.7, focusing on temporal types 在MySQL 5.6.4中,对TIME, TIMESTAMP and DATETIME三种时 ...
- Quartz 在 Spring 中如何动态配置时间--转
原文地址:http://www.iteye.com/topic/399980 在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度. 有关调度的实现我就第一就想到了Quartz这个开源 ...