C#异常性能影响
何谓异常
很多人在讨论异常的时候很模糊,仿佛所谓异常就是try{}catch{},异常就是Exception,非常的片面,所以导致异常影响性能,XXXX……等很多奇怪的言论,所以在此我意在对异常正名。以下,我将异常这个很宽泛,容易被曲解的词进行严格的划分。
异常机制
所谓异常机制也就是指的语言平台支持异常这种错误处理模式的机制,比如c#里的Exception对象,try{}catch{}finally{}结构,throw抛出异常的语句,等等,均为c#语言里对异常机制的实现。
异常机制是随着语言而存在的,一种语言既然支持异常机制,那么异常就是不可回避的,哪怕你自己不throw异常,你所使用的系统类,也会抛出异常给你,所以说讨论在系统里用不用异常是非常可笑的事情。异常机制就像系统的后门,当一个过程执行中系统出错的时候,或者你认为系统不正常的时候,就把当时的情况拍个快照生成异常对象,通过特殊的通道通知调用这个过程的方法。
异常对象
异常对象是异常机制中用来描述异常,记录错误信息,可以说是错误发生是的快照,就跟你开快车闯红灯被天眼拍到的照片差不多。
抛出异常
所谓抛出异常就是异常机制中通知调用的方法本方法出错了的手段。而抛出异常也是很多人诟病异常对性能影响的地方,因为系统性能的开销都是由抛出异常所引起的。但是抛出异常也无法避免,因为这是系统本身的特点,你不抛出异常,系统自己也会抛出,一旦系统抛出了异常对象,你怎么来避免这个性能的开销?没辙
比如方法A调用了方法B,方法B访问数据库,出错了,那么B就会抛出异常,而A就需要catch这个异常来处理。
抛出异常有两种形式,一种是系统抛出的异常,一种是我们自己认为在处理一段逻辑的时候当逻辑错误,就可以通过抛出一个异常对象,来通知调用这个方法的方法,这里出错了。
系统抛出异常
系统抛出的异常对编程人员来说是透明的,也就是我们不需要关心系统是如何得知出错了的,系统类库一旦出错就会将异常对象抛给我们调用它的方法,因为系统本身并不知道要如何处理这个错误。
用户抛出异常
用户抛出异常通常在自己写类库,定义一套API给别人使用的时候用到,这个时候我们并不知道要如何处理这些逻辑错误,所以就需要交给知道如何处理逻辑错误的方法去处理。
在C#里通过定义了throw关键字来抛出异常对象,这里除了抛出新创建的异常对象,也可以通过将已经catch到的异常重新抛出。如果不知道如何处理这个异常,重新抛出异常是很好的习惯,因为既然已经抛出了异常,那么性能已经损失了,所以也不在乎多这么一丁点来更好的挽回错误的结果。
处理异常
所有的异常必须得到妥善的处理,也就是说你必须处理所有的异常。因为系统出错就抛异常,所以这个是c#骨子里的东西,无法避免,所以,系统里到处都会充斥着try{}catch{}。
但是try{}catch{}本身并不会影响系统的性能,所以在没有异常发生的时候try{}catch{}是不会让你系统变慢的,而一旦发生系统异常,你不处理系统就崩溃掉了,你到底是愿意系统慢一点点然后处理掉这个错误呢还是愿意系统崩溃掉呢?
对于处理异常这一点其实我觉得java那种比较严格的,要求严格声明并处理所有异常的方式比较好,能够强制让你重视起异常这回事来,免得系统崩溃掉才想起自己没处理这个异常。
如何处理
如何正确的处理异常?这是个很多c#程序员都没最终搞明白的话题,很多人所了解的也无非就是不要用异常处理当业务逻辑,但是何谓用异常来处理逻辑就不得而知了。这里我来详细说明一下,什么地方要用异常,什么地方不要用。
首先有一个前提,异常机制是一个由底向上的冒泡的过程,所以正常的逻辑是,异常由底层抛出,由高层来处理。
处理异常正确的例子
要编写健壮的引用程序,首先要保证必须处理所有的系统异常,也就是调用.NET类库的方法的时候,这个方法可能抛出的异常
例:
![]()
我们可以看到,GetResponse方法会抛出两个异常 InvalidOperationException和WebException。那么我们就需要将调用的代码所在的catch单独处理这两个异常
![]()
如果你在这个方法中不知道应该如何处理这个异常,比如这个方法不上不下,和表现层离了好几层,而又需要在表现层通知用户或者由上层业务来决定是通知用户还是悄悄的进村,又或者是悄悄的重新尝试一次,那么自己不能决定的事情就要抛给上层去处理。
![]()
有人可能会说.NET可以统一处理异常的,不过我不推荐那种大而全的处理方式,不够细致,很多时候会被奇怪的错误搞得你很追查错误的本源。且比如说遇到网络问题重试就没法处理。
异常必须就近处理,这样才能方便追查,而且注意那个“xxx方法出错了”那个地方,很重要,这个描述可以让你在系统排错的时候少走很多弯路,尽量写详细点。
如果你的代码是给别的程序员使用,而不是和最终用户直接交互,那么除了处理所有系统抛出的异常以外,还需要用异常来验证过滤入口参数。比如,假设你要给其他程序员提供一个将用户对象插入数据库的方法:
public void InsertUser(User user)
{
if(user==null)
{
throw new ArgumentNullException("参数user为null");
}
//调用Orm }
这里我们验证了入口参数,并尽早的抛出了异常,因为这里抛出的异常和不处理等db操作抛出异常,肯定是这里手动抛出的开销更小。这里有一个原则就是,如果这个参数会造成底层代码直接出错,那么就就近处理它,而不要放任其在底层造成系统异常的抛出。当然还有一个原则就是不要在这里判断业务逻辑,比如上面的例子就不要在这里验证User的属性的数值是不是合法之类的。
处理异常错误的例子
1:用异常验证用户输入
用户输入的合法性验证是属于业务逻辑的一部分,绝对不要用异常去处理,注意,是用户输入,所以这个经验仅限于表现层逻辑
典型的错误1:
try
{
int i=int.Parse(textBox1.Text);
}
catch(Exception ex)
{
alert(“不要输入非数字”);
}
典型错误2:
void ValidateInput(int i)
{
if(i<0&&i>100)
{
throw new Exception("输入数据范围错误");
}
}
以上两种错误都是错误使用异常的典型,
2:将异常延迟到底层
这一点我们在正确的例子里提到过
典型错误3:
try
{
string name=Request.QueryString["xx"];
List<User> userls=User.QueryUserByName(name);
}
catch(SqlException ex)
{
}
这个错误在于完全不验证用户输入而直接把数据的验证抛向数据库,等待数据库报错来判断用户输入的正确性,这个是非常致命的错误,很多注入漏洞都是由此产生的。
3:完全不用异常机制
产生这个错误肯定是一个非常脑残的决定造成的。不过很多时候某些不了解异常机制的人,由于对异常的性能开销的恐惧感,经常会做出这么脑残的决定。
典型错误4:
public bool InsertUser(User user,ref int errcode)
{
if(user==null)
{
errcode=110;//参数为空错误的代码 return false;
}
//调用Orm }
感觉就是一夜回到了解放前,性能倒是高了,但是系统异常怎么办呢?一旦数据库出错就只等着系统崩溃了。某些有经验的说我会把下面的try{}catch{}起来,不过那不是脱了裤子放屁么,异常都抛出来了,开销已经产生了,结果换来的是牺牲了异常对象的丰富信息而换来了畸形的系统逻辑。性能也没得到提高。
异常对性能的影响
异常机制是C#的特征,因此决定了你不可能逃避,所以讨论异常给你带来了多大开销都是扯淡,没有必要的研究,从根本上无法解决问题。我们应该弄清楚的是,异常的抛出给系统带来什么样的影响,如何在保证系统健壮性的基础上减小不必要的性能消耗。
1.异常的性能开销随着调用栈的深度增加而增大。
对比测试:
测试代码1
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10; i++)
{
try
{
throw new Exception("test");
}
catch { }
}
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds + "ms");
测试结果:
| 调用次数 | 第一次 | 第二次 | 第三次 | 第四次 |
| 时间 |
测试代码2
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10; i++)
{
try
{
System.IO.File.OpenRead("c:\\不存在的txt.txt");
}
catch { }
}
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds + "ms");
测试结果
| 调用次数 | 第一次 | 第二次 | 第三次 | 第四次 |
| 时间 |
小结:由此我们可以发现随着调用栈的深入,性能开销也越大,所以异常应该尽早抛出。
2.对输入的数据应该在业务逻辑中严格检查。
3.try{}catch{}不会造成任何的系统开销,造成系统开销的是throw 抛出异常,这是再三强调的了
诸如:
![]()
这种言论就纯属脑残了。
结论
异常机制是C#内置的错误处理机制,你无法避免它,唯一正确的道路是学会如何正确使用
转载:http://www.mamicode.com/info-detail-99312.html
C#异常性能影响的更多相关文章
- .net core 抛异常对性能影响的求证之路
一.前言 在.net 社区中曾经听到过很多关于大量抛异常会影响性能这样的结论,心中一直就存在各种疑问.项目中使用自定义异常来处理业务很爽,但是又担心大量抛业务异常存在性能问题. 查阅了各种文档,微软官 ...
- 盘点 Oracle 11g 中新特性带来的10大性能影响
Oracle的任何一个新版本,总是会带来大量引人瞩目的新特性,但是往往在这些新特性引入之初,首先引起的是一些麻烦,因为对于新技术的不了解.因为对于旧环境的不适应,从Oracle产品到技术服务运维,总是 ...
- .Net Discovery系列之十二-深入理解平台机制与性能影响(下)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制.即时编译机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的异常捕获机制与字符串驻留机制. 三.关于异常捕获机制 虽然我们已经很 ...
- 对MySQL性能影响较大的五类配置参数
以下主要是对MySQL 性能影响关系紧密的五大配置参数的介绍. 一. 连接 连接通常来自Web 服务器,下面列出了一些与连接有关的参数,以及该如何设置它们. (一). ...
- 考查SQLite 3索引对整数排序的性能影响
做个实验,想了解SQLite3索引对整数排序的性能影响. 用这个测试表,考查绿色那列: id name date 自增型主键 字符串型,随机生成 整数型 随机生成,范围0到54354354 1 bMz ...
- Salesforce select字段的多少对性能影响巨大
Salesforce select字段的多少对性能影响巨大,第1个是select 144个字段,第2个是select 5个字段, 性能相差了7倍 "select Id,IsDeleted,M ...
- mysql中in和exists二者的区别和性能影响
mysql查询语句in和exists二者的区别和性能影响 还记得一次面试中被人问到in 和 exists的区别,当然只是草草做答,现在来做下分析. mysql中的in语句是把外表和内表作hash 连接 ...
- numa对MySQL多实例性能影响
numa对MySQL多实例性能影响,通过对numa将MySQL绑定在不同的CPU节点上,并且采用绑定的内存分配策略,强制在本节点内分配内存.具体测试如下:1.关闭numa(numa= interle ...
- ToList<>()所带来的性能影响
ToList<>()所带来的性能影响 前几天优化师弟写的代码,有一个地方给我留下很深刻的印象,就是我发现他总是将PLINQ的结果ToList<>(),然后再返回给主程序,对于 ...
随机推荐
- 企业架构与建模之ArchiMate的由来和详述(上)
终于完成了关于企业架构框架理论的总结,谢谢各位看官的支持,能挺过之前过于理论化的叙述而坚持到现在着实不易,笔者也自愧没有实践经验可以分享,希望日后有兴趣的看官能够不吝赐教.在本系列后面的也是最后一个大 ...
- javascript遍历select下拉框判断其中值是否与指定值相等
用jquery多了,就忘了原生的js是如何写的了,还需要多加巩固. 需求:jsp回显一select下拉框.选中指定值. 用户点击修改 该select进行已有值回显.有两种解决方法 一.js中获取用户的 ...
- SVN 在 Xcode中的状态说明
最近同事总是问我关于SVN状态的问题,‘C’是什么意思啦?‘A’是什么意思啦?等等一系列问题. 为了方便以后查阅,以及新同事的快速融入,特在此记录一下^_^. 当然了大家也可以google一下,一搜一 ...
- Discuz开源论坛
Discuz开源论坛本地部署自动生成数据库 这个版本可能比较有点老,但是万变不离其宗,再新的版本都是在已有的基础上更新的,所以掌握方法是最重要的! 先上几张安装成功后的图 (安装成功的论坛首页 ...
- MySQL中函数、游标、事件、视图
MySQL中函数.游标.事件.视图基本应用举例(代码) MySQL中function用户自定义函数c,fun,fun是面向过程的实现方式只能传入参数,或不传入参数,不能传出参数,必有返回值函数中是不能 ...
- 一步一步深入spring(2)-三种方式来实例化bean
在一步一步深入spring(1)--搭建和测试spring的开发环境中提到了一种实例化bean的方式,也是最基本的使用构造器实例化bean 1.使用构造器实例化bean:这是最简单的方式,Spring ...
- Remobjects使用经验
RemObjects RemObjects提示:我们相信本文是正确的,但我们不做任何保证.在此感谢Henrick 写的文章,很高兴在此发表. 介绍RemObjects是功能强大可扩展的远程框架;但是当 ...
- 用Linux命令行实现删除和复制指定类型的文件
(一)Linux 删除当前目录及子目录中所有某种类型的文件 方法1 : 此方法不能处理目录中带空格的那些. rm -rf `find . -name "*.example"` Li ...
- 关于oracle12c对RAW裸设备的支持?
关于oracle12c对RAW裸设备的支持? 本文内容由ORACLE运维高级群的讨论,有xifenfei前辈提供. 12C对于裸设备的支持和11G R2没有本质区别,在装rac的时候不能使用裸设备,但 ...
- 基于Spark的用户行为路径分析
研究背景 互联网行业越来越重视自家客户的一些行为偏好了,无论是电商行业还是金融行业,基于用户行为可以做出很多东西,电商行业可以归纳出用户偏好为用户推荐商品,金融行业可以把用户行为作为反欺诈的一个点,本 ...