.NET:何时应该 “包装异常”?
背景
提到异常,我们会想到:抛出异常、异常恢复、资源清理、吞掉异常、重新抛出异常、替换异常、包装异常。本文想谈谈 “包装异常”,主要针对这个问题:何时应该 “包装异常”?
“包装异常” 的技术形式
包装异常是替换异常的特殊形式,具体的技术形式如下:
try
{
// do something
}
catch (SomeException ex)
{
throw new WrapperException("New Message", ex);
}
注意:WrapperException 需要将 ex 作为 InnerException,这样才不至于丢失 StackTrace,WrapperException.StackTrace 和 ex.StackTrace 共同构成了完整的 StackTrace。
让例子帮助我们得出答案
有这样一种场景:我希望为各种 ORM 框架提供一种抽象,这可以让应用开发人员自由的在不同的 ORM 实现之间做出选择。
第一个版本的实现
实现伪代码
interface IRepository<TEntity>
{
void Update(TEntity entity);
} class EntityFrameworkRepository<TEntity> : IRepository<TEntity>
{
public void Update(TEntity entity)
{
throw new EntityFrameworkConcurrentException(); // 可能会抛出这样的异常,这里的代码不是十分准确。
}
} class NHibernateRepository<TEntity> : IRepository<TEntity>
{
public void Update(TEntity entity)
{
throw new NHibernateConcurrentException(); // 可能会抛出这样的异常,这里的代码不是十分准确。
}
}
有什么问题?
处理并发异常是应用层开发人员的一个非常重要的职责,他们或者选择自动重试、或者选择让用户重试、甚至允许并发带来的不一致性,如果使用了上面的接口问题就大了,应用中该拦截哪种并发异常呢?EntityFrameworkConcurrentException?NHibernateConcurrentException?这样的接口和实现无论如何都达不到:OCP 和 LSP。
将异常作为契约的一部分
实现伪代码
interface IRepository<TEntity>
{
void Update(TEntity entity);
} class ConcurrentException : Exception
{
} class EntityFrameworkRepository<TEntity> : IRepository<TEntity>
{
public void Update(TEntity entity)
{
try
{
}
catch (EntityFrameworkConcurrentException ex)
{
throw new ConcurrentException(ex);
}
}
} class NHibernateRepository<TEntity> : IRepository<TEntity>
{
public void Update(TEntity entity)
{
try
{
}
catch (NHibernateConcurrentException ex)
{
throw new ConcurrentException(ex);
}
}
}
有什么问题?
目前来说还觉得不错,如果 C# 编译器或 CLR 能支持异常契约就好了,Java 虽然支持,但是对于调用者来说又不太友好。
这里给出答案
当异常是契约的一部分时,才需要包装异常。
可能还会有其它答案,等我再思考思考,朋友们也可以给出一些想法。
微软的一个反例
MethodBae.Invoke
// System.Reflection.TargetInvocationException:
// 调用的方法或构造函数引发异常。
//
// System.MethodAccessException:
// 调用方没有调用此构造函数的权限。
//
// System.InvalidOperationException:
// 声明此方法的类型是开放式泛型类型。 即,System.Type.ContainsGenericParameters 属性为声明类型返回 true。
public abstract object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture);
当方法内部抛出异常时,Invoke 会将内部异常给包装起来,这明显不是我们期望的行为,后来微软的 dynamic 调用 和 CreateDelegate 之后使用 Delegate 调用 都修复了这个问题。
备注
最近在读第四版的 clr via c#,确实是一部好书,关于异常作为契约部分的想法,和作者产生了很大的共鸣,书中对异常处理的讲解非常细致,推荐大家读一读这本书。
.NET:何时应该 “包装异常”?的更多相关文章
- C# 插件热插拔 .NET:何时应该 “包装异常”? log4.net 自定义日志文件名称
C# 插件热插拔 所谓热插拔就是插件可以 在主程序不重新启动的情况直接更新插件, 网上有很多方案: https://www.cnblogs.com/happyframework/p/3405811 ...
- 编写高质量代码改善C#程序的157个建议[避免finaly内的无效代码、避免嵌套异常、避免吃掉异常、注意循环异常处理]
前言 本文已同步到http://www.cnblogs.com/aehyok/p/3624579.html.本文主要来学习以下几点建议 建议61.避免在finally内撰写无效代码 建议62.避免嵌套 ...
- 编写高质量代码改善C#程序的157个建议[用抛异常替代返回错误、不要在不恰当的场合下引发异常、重新引发异常时使用inner Exception]
前言 自从.NET出现后,关于CLR异常机制的讨论就几乎从未停止过.迄今为止,CLR异常机制让人关注最多的一点就是“效率”问题.其实,这里存在认识上的误区,因为正常控制流程下的代码运行并不会出现问题, ...
- JAVA异常使用_每个人都曾用过、但未必都用得好
一.抛出异常 vs. 返回错误代码 有人说“Well, an exception is a goto.”,但也有人言“makes the code simpler by visibly separat ...
- 你真的会阅读Java的异常信息吗?
给出如下异常信息: java.lang.RuntimeException: level 2 exception at com.msh.demo.exceptionStack.Test.fun2(Tes ...
- Java在Service层异常封装
dao层不需要抛出异常,应该在service层抛出异常,可以是自定义的异常,也可以包装异常,然后在controller中定义exception handler统一处理或者单独处理. 参考: https ...
- C# 异步编程Task整理(二)异常捕捉
一.在任务并行库中,如果对任务运行Wait.WaitAny.WaitAll等方法,或者求Result属性,都能捕获到AggregateException异常. 可以将AggregateExceptio ...
- Java继承Exception自定义异常类教程以及Javaweb中用Filter拦截并处理异常
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6403033.html 在项目中的应用见: https://github.com/ygj0930/CoupleS ...
- Java中处理异常的9个最佳实践
Java中的异常处理不是一个简单的话题.初学者很难理解,甚至有经验的开发人员也会花几个小时来讨论应该如何抛出或处理这些异常. 这就是为什么大多数开发团队都有自己的异常处理的规则和方法.如果你是一个团队 ...
随机推荐
- 微信小程序地图模块
微信小程序的地图模块官方提供的API比较少,详情请见 官方文档 以下为一个示例 <!--pages/location/locati ...
- HBase(二)CentOS7.5搭建HBase1.2.6HA集群
一.安装前提 1.HBase 依赖于 HDFS 做底层的数据存储 2.HBase 依赖于 MapReduce 做数据计算 3.HBase 依赖于 ZooKeeper 做服务协调 4.HBase源码是j ...
- Python全栈开发之21、django
http://www.cnblogs.com/wupeiqi/articles/5237704.html http://www.cnblogs.com/wupeiqi/articles/5246483 ...
- 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]
题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...
- CodeForces - 725D Contest Balloons 贪心
D. Contest Balloons time limit per test 3 seconds memory limit per test 2 ...
- JDK源码分析(三)——HashMap 下(基于JDK8)
目录 概述 内部字段及构造方法 哈希值与索引计算 存储元素 扩容 删除元素 查找元素 总结 概述 在上文我们基于JDK7分析了HashMap的实现源码,介绍了HashMap的加载因子loadFac ...
- 【基础知识】.Net基础加强第三天
一. 里氏替换原则--类型转换 1. 里氏替换原则:当需要一个父类类型对象的时候,可以给一个子类类型的对象. 2. 里氏替换原则实际也就是发生了隐身转换 3. a.>把子类类型赋值给父类类型, ...
- [leetcode DP]91. Decode Ways
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...
- 用 Python 实现一个大数据搜索引擎
搜索是大数据领域里常见的需求.Splunk和ELK分别是该领域在非开源和开源领域里的领导者.本文利用很少的Python代码实现了一个基本的数据搜索功能,试图让大家理解大数据搜索的基本原理. 布隆过滤器 ...
- 最新OFFICE 0day漏洞分析
漏洞概述 fireeye最近公布了一个OFFICE 0day,在无需用户交互的情况下,打开word文档就可以通过hta脚本执行任意代码.经过研究发现,此漏洞的成因主要是word在处理内嵌OLE2LIN ...