从AggregateException看异常类的设计
异常是程序在有bug时最直观的表现形式,不担心有bug存在,而担心bug埋没在大堆的代码中而发现不了。
这篇随笔简单谈谈从AggregateException类源码(http://www.projky.com/dotnet/4.5.1/System/AggregateException.cs.html)中窥出的.NET Framework类库设计的方式。
总结有以下几点:
1、特性的使用:DebuggerDisplayAttribute,SerializableAttribute
2、只读属性的运用
3、简单的队列算法
AggregateException主要用在TPL库中,在等待时发生异常的Task可能不止一个,所以,设计通过InnerExceptions属性访问所有的异常。需要特别注意的是,Exception对象本身有一个InnerException属性,是单数的,少了个s,最多只能记录一个异常。先来看看它的一个典型用法:
int[] locations = GetCacheLocations();
var readCacheTasks = new Task<CacheItem>[locations.Length];
for (int i = ; i < locations.Length; i++) {
int location = locations[i];
readCacheTasks[i] = new Task<CacheItem>(ReadDistributeCache, location);
readCacheTasks[i].Start();
} try {
Task.WaitAll(readCacheTasks);
for(int i = ; i < locations.Length; i++){
ProcessCacheItem(readCacheTasks[i].Result, i);
}
} catch (AggregateException ae) {
ae.Handle(e => {
return e is NotFoundException;
});
//throw ae.Flatten();
}
这段代码中,如果读取分布式缓存的多个任务发生了异常,也能及时确定是否存在一个bug。
从AggregateException的源码看,只有两个特性声明在该类上,
[Serializable]
[DebuggerDisplay("Count = {InnerExceptionCount}")]
public class AggregateException : Exception {
private int InnerExceptionCount{
get{
return InnerExceptions.Count;
}
} ......
}
DebuggerDisplayAttribute特性让我们在下断点调试时,鼠标位置在该类上出现的提示。例如,包含了3个内部异常,那么在断点提示是会是“Count = 3”,这里访问了私有的属性,不一定要私有的,但私有的成员在写代码时不会出现在代码提示中,减少了干扰。
SerializableAttribute特性让该类支持序列化,比如序列化成xml文件、流、json等格式,再反序列化得到该类对象。仅仅声明该属性是不够的,还添加实现两个成员,一是在序列化过程中,要往结果中存什么,GetObjectData方法,二是支持反序列化的构造函数。
public override void GetObjectData(SerializationInfo info, StreamingContext context){
base.GetObjectData(info, context);
Exception[] innerExceptions = new Exception[m_innerExceptions.Count];
m_innerExceptions.CopyTo(innerExceptions, );
info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[]));
} protected AggregateException(SerializationInfo info, StreamingContext context) : base(info, context){
Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[];
m_innerExceptions = new ReadOnlyCollection<Exception>(innerExceptions);
}
字符串“InnerExceptions”只是一个名字而已,相当于一个key,不同的属性,key必须不同,同时,还得避免继承可能导致的key重复问题。
大部分情况下,使用AggregateException都是访问它的InnerExceptions属性,
private ReadOnlyCollection<Exception> m_innerExceptions; // Complete set of exceptions. public ReadOnlyCollection<Exception> InnerExceptions{
get { return m_innerExceptions; }
} private AggregateException(string message, IList<Exception> innerExceptions)
: base(message, innerExceptions != null && innerExceptions.Count > ? innerExceptions[] : null)
{
Exception[] exceptionsCopy = new Exception[innerExceptions.Count];
for (int i = ; i < exceptionsCopy.Length; i++){
exceptionsCopy[i] = innerExceptions[i];
if (exceptionsCopy[i] == null){
throw new ArgumentException(Environment.GetResourceString("AggregateException_ctor_InnerExceptionNull"));
}
}
m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy);
}
它的InnerExceptions属性是只读的,避免外部访问修改,而且在构造该AggregateException对象实例时,对参数检验和直接的浅拷贝,尽量使整个过程只在返回时修改类的状态,一般情况下,可以认为该过程没有副作用。
如果你仔细想想,可能在InnerExceptions中,也存在AggregateException对象,所以,专门提供了一个Flatten方法,来提取层级中所有的非AggregateException对象到一个ReadOnlyCollection<Exception>对象中,字面意思理解就是扁平化。
public AggregateException Flatten()
{
// Initialize a collection to contain the flattened exceptions.
List<Exception> flattenedExceptions = new List<Exception>(); // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue
List<AggregateException> exceptionsToFlatten = new List<AggregateException>();
exceptionsToFlatten.Add(this);
int nDequeueIndex = ; // Continue removing and recursively flattening exceptions, until there are no more.
while (exceptionsToFlatten.Count > nDequeueIndex){
// dequeue one from exceptionsToFlatten
IList<Exception> currentInnerExceptions = exceptionsToFlatten[nDequeueIndex++].InnerExceptions; for (int i = ; i < currentInnerExceptions.Count; i++){
Exception currentInnerException = currentInnerExceptions[i]; if (currentInnerException == null){
continue;
} AggregateException currentInnerAsAggregate = currentInnerException as AggregateException; // If this exception is an aggregate, keep it around for later. Otherwise,
// simply add it to the list of flattened exceptions to be returned.
if (currentInnerAsAggregate != null){
exceptionsToFlatten.Add(currentInnerAsAggregate);
}
else{
flattenedExceptions.Add(currentInnerException);
}
}
} return new AggregateException(Message, flattenedExceptions);
}
这段代码实现了一个FIFO队列,但并不是传统意义上的队列,没有出队列,只有入队列,通过一个递增索引记录处理到哪一个元素了。仔细琢磨,设计得还是挺不错的,简单实用。
从AggregateException看异常类的设计的更多相关文章
- 通过寄生组合式继承创建js的异常类
最近项目中在做js的统一的异常处理,需要自定义异常类.理想的设计方案为:自定义一个异常错误类BaseError,继承自Error,然后再自定义若干个系统异常,例如用户取消异常.表单异常.网络异常,这些 ...
- Core Java 总结(异常类问题)
所有代码均在本地编译运行测试,环境为 Windows7 32位机器 + eclipse Mars.2 Release (4.5.2) 2016-10-17 整理 下面的代码输出结果是多少?为什么?并由 ...
- [三]JavaIO之IO体系类整体设计思路 流的概念以及四大基础分类
从本文开始,将正式进入JavaIO的简介 在继续javaIO系列的文章之前 可以过去看一下 本人博客上的设计模式中的 适配器模式和装饰器模式 这会对接下来的阅读大有帮助 本文是从逻辑上介绍整个的J ...
- Atitit.异常机制的设计原理
Atitit.异常机制的设计原理 缺陷 关键是只要知晓有一个异常表的存在,try 的范围就是体现在异常表行记录的起点和终点.JVM 在 try 住的代码区间内如有异常抛出的话,就会在当前栈桢的异常表中 ...
- 类的继承与super()的意义以即如何写一个正确的异常类
这些东西都是我看了许多名师课程和自己研究的成果,严禁转载,这里指出了如何正确的自己定义一个异常类并看一看sun写的java的源代码话题一:子类的构造器执行是否一定会伴随着父类的构造执行? 1.this ...
- 012医疗项目-模块一:统一异常处理器的设计思路及其实现(涉及到了Springmvc的异常处理流程)
我们上一篇文章是建立了一个自定义的异常类,来代替了原始的Exception类.在Serice层抛出异常,然后要在Action层捕获这个异常,这样的话在每个Action中都要有try{}catch{}代 ...
- Java最重要的21个技术点和知识点之JAVA集合框架、异常类、IO
(三)Java最重要的21个技术点和知识点之JAVA集合框架.异常类.IO 写这篇文章的目的是想总结一下自己这么多年JAVA培训的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享 ...
- Java开源生鲜电商平台-异常模块的设计与架构(源码可下载)
Java开源生鲜电商平台-异常模块的设计与架构(源码可下载) 说明:任何一个软件系统都会出现各式各样的异常与错误,我们需要根据异常的情况进行捕获与分析,改善自己的代码,让其更加的稳定的,快速的运行,那 ...
- C#编程(八十)---------- 异常类
异常类 在C#里,异常处理就是C#为处理错误情况提供的一种机制.它为每种错误情况提供了定制的处理方式,并且把标志错误的代码预处理错误的代码分离开来. 对.net类来说,一般的异常类System.Exc ...
随机推荐
- 解析ASP.NET Mvc开发之EF延迟加载 分类: ASP.NET 2014-01-04 01:29 4017人阅读 评论(1) 收藏
目录: 从明源动力到创新工场这一路走来 解析ASP.NET WebForm和Mvc开发的区别 解析ASP.NET 和Mvc开发之查询数据实例 ----------------------------- ...
- 小程序api-01-abcdefg
目录-abcdefg wx.scanCode(OBJECT) 调起客户端扫码界面,扫码成功后返回对应的结果 wx.scanCode({ success: (res) => { console ...
- 补间动画Tweened Animations
本例子简单讲一下怎么用补间动画 1.在xml中定义好动画的资源文件,如下(注意把不同的效果放在一起可以一起用,同时起效果) <?xml version="1.0" encod ...
- PCI配置空间简介
一.PCI配置空间简介 PCI有三个相互独立的物理地址空间:设备存储器地址空间.I/O地址空间和配置空间.配置空间是PCI所特有的一个物理空间.由于PCI支持设备即插即用,所以PCI设备不占用固定的内 ...
- elasticsearch(六) 之 elasticsearch优化
目录 elasticsearch 优化 从硬件上 : 从软件上: 从用户使用层 elasticsearch 优化 从硬件上 : 使用SSD 硬盘,解决io导致的瓶颈. 增大内存 但不超过32G(单实例 ...
- Go RabbitMQ (一)
RabbitMQ 简介 RabbitMQ是一个消息代理,用来负责接收和转发消息. 术语 生产者:生产者是负责发送消息的 队列:队列是RabbitMQ用来存储消息的,受主机内存和磁盘大小的限制,本质上是 ...
- java Fork/Join框架
应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在很多的应用程序在运行在多核心 ...
- centOS7.2下 搭建gitlab使用git为团队管理代码
最近更换了阿里云服务器 使用centOS7.2,目前配置1核2G,搭建gitlab有点吃力,另外如果1核1g就不要搭建了,推荐配置是2核4G以上 下面来简单记录整个搭建过程 注意: 本次实验OS为c ...
- Node.js之HTTP请求与响应
在C#.OC中也是客户端发起一个请求,服务端作出响应.我们可以把这个过程抽象理解 . 1.客户端给服务端发起请求相当于向服务端写入一个流(writable) 2.服务端读取客户端的流(readable ...
- 使用iTextSharp 解析html生成pdf,xmlworker不支持中文的解决办法
http://www.micmiu.com/opensource/expdoc/itext-xml-worker-cn/ 参考上面的文章,虽然是java的,但是和.net是对应的. 下载 html ...