惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
H
Help Net Security
V
Visual Studio Blog
J
Java Code Geeks
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
The Cloudflare Blog
Martin Fowler
Martin Fowler
D
Docker
腾讯CDC
F
Fortinet All Blogs
雷峰网
雷峰网
GbyAI
GbyAI
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
博客园 - 聂微东
博客园 - 叶小钗

博客园 - 涵树

Rust中使用RocksDB索引进行高效范围查询的实践指南 代码的形状:重构的方向 笔记系统开源地址 前端和后端开发的异同 micro-service构建平台 发布到NPMJS 前端开发的思考--看起来简单,做起来难 足迹 星际迷航3 -- 父亲的纪念 数据建模与框架设计的暂时总结 一次“任性”的编码 项目管理的持续思考 敏捷中的沟通与故事点 RequireJS+JQueryMobile 项目管理:人 工厂方法(Factory Method),思考 寻觅[Getting Answers] 我所知道的Javascript 问“为什么”
断言与异常(Assertion Vs Exception)
涵树 · 2013-12-29 · via 博客园 - 涵树

在日常编程实践中,断言与异常的界限不是很明显,这也使得它们常常没有被正确的使用。我也在不断的与这个模糊的怪兽搏斗,仅写此文和大家分享一下我的个人看法。我想我们还可以从很多角度来区别断言和异常的使用场景,欢迎大家的意见和建议。

异常的使用场景:用于捕获外部的可能错误

断言的使用场景:用于捕获内部的不可能错误

我们可以先仔细分析一下我们在.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上可能出问题,那我们应该如何处理这个问题呢?在这个上下文中,我们别无它法,只能让这个错误继续往上抛,通知上面一层的调用者,有一个错误发生了,至于上一层调用者会如何处理,不是这个函数要考虑的问题。但在这个函数中,要记得一点,将当前函数中所占用的资源释放了。因此,当我们不能控制的外部错误出现时,我们可以将其作为异常往上抛,这时,我们该使用异常。

现在再来看看断言,我们还是以下面的一段代码为例子。

 1         public Entities.SimpleBugInfo GetSimpleBugInfo(string bugNum)
 2         {
 3 
 4             var selector = DependencyFactory.Resolve<ISelector>();
 5 
 6             var list = selector.Return<Entities.SimpleBugInfo>(
 7                 reader => new Entities.SimpleBugInfo
 8                 {
 9                     bugNum = reader["bugNum"].ToString(),
10                     dealMan = reader["dealMan"].ToString(),
11                     description = reader["description"].ToString(),
12                     size = Convert.ToInt32(reader["size"]),
13                     fired = Convert.ToInt32(reader["fired"]),
14                 },
15                 "select * from bugInfo",
16                 new WhereClause(bugNum, "bugNum"));
17 
18             Trace.Assert(list != null);
19             
20             if (list.Count == 0)
21                 return null;
22             else
23                 return list[0];
24 
25         }

当我贴出这段代码时,心情有些坎坷,因为我本人在这里也纠结了很久,这也是我一直没有将断言和异常划清界线的原因之一。

首先我们来回顾一下之前定义的断言使用场景:内部不可能发生的错误。

selector.Return这段代码是不是内部代码?如果我们能够修改Return中的代码,说明它是内部代码;反之,说明它是外部代码。对于内部代码,我们可以用断言来保护其逻辑的不变性,当断言被触发时,我们就可以确信是内部代码的错误,我们应该立即修复。

再纠结一下,假设Return是外部代码,我们没有办法去修改它。那么上面的代码可以有两种写法(如果你有更多的想法,请赐教)。

第一种,直接抛出异常。

If(list == null)
{
    throw new NullReferenceException();
}

第二种,调整代码。

if(list == null || list.Count == 0)
{
    return null;
}
else
{
    return list[0];
}

当然,还有一种就是什么也不做,让代码执行下去直至系统为你抛出空引用错误。但这种做法违背了防卸性编程的原则,我们总是应行尽早或离错误的发生地最近的地方处理错误,避免错误数据流向系统的其它地方,产生更加严重的错误。

总结

对异常或断言的使用取决于你要防卸的是一个内部错误还是外部错误以及你认为它是一个内部错误或外部错误。如果你决定防卸一个内部错误,那请果断使用断言,反之,请使用异常。

参见:

.net 的异常继承树(http://msdn.microsoft.com/en-us/library/z4c5tckx(v=vs.110).aspx)

原代码来至于我的TeamView开源项目(http://teamview.codeplex.com)

巜从小工到大师》