.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中的异常处理不是一个简单的话题.初学者很难理解,甚至有经验的开发人员也会花几个小时来讨论应该如何抛出或处理这些异常. 这就是为什么大多数开发团队都有自己的异常处理的规则和方法.如果你是一个团队 ...
随机推荐
- javascript重要类方法笔记
三.数据结构和map 1.大括号数据结构:{} 1.1 键值对形式,类似Map 1.2 var treeNode={}; treeNode ...
- Kafka(一)Kafka的简介与架构
一.简介 1.1 概述 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/ng ...
- Cordova 打包签名
1.第一步 在项目根目录下运行命令cordova build --release android,会在testApp\platforms\android\build\outputs\apk目录下生成一 ...
- 接口调用 POST
/** * 接口调用 POST * @return [type] [description] */ public function portPhone(Request $request) { $pho ...
- Wannafly挑战赛9 D - 造一造
链接:https://www.nowcoder.com/acm/contest/71/D来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...
- volatile 和锁的内存语义
一.volatile 的内存语义 1. volatile 的特性 volatile变量自身具有以下特性: 可见性 :对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后 ...
- ajax异步请求模式
什么是异步请求 我们知道,在同步请求模型中,浏览器是直接向服务器发送请求,并直接接收.处理服务器响应的数据的.这就导致了浏览器发送完一个请求后,就只能干等着服务器那边处理请求,响应请求,在这期间其它事 ...
- Java中面向对象的理解
按照惯例,先做一个简单的介绍,现在开始学习 Thinging in Java 4 ,一边看,一边记录,我都不想给自己设定时间安排了,毕竟很少实现过.所以就这样吧!不定期的更新,我都会放到博客中的. 所 ...
- mysql高性能索引
独立索引: 独立索引是指索引列不能是表达式的一部分,也不能是函数的参数 例1: SELECT actor_id FROM actor WHERE actor_id+1=5 --这种写法,就算在acto ...
- [BZOJ1758][WC2010]重建计划(点分治+单调队列)
点分治,对于每个分治中心,考虑求出经过它的符合长度条件的链的最大权值和. 从分治中心dfs下去取出所有链,为了防止两条链属于同一个子树,我们一个子树一个子树地处理. 用s1[i]记录目前分治中心伸下去 ...