知识大全 .NET的异常处理的几个误区

Posted

篇首语:卧疾丰暇豫,翰墨时间作。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 .NET的异常处理的几个误区相关的知识,希望对你有一定的参考价值。

.NET的异常处理的几个误区  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  Net出现多年之后还是对异常处理一知半解的 有很多误解 本文将讲解三个常见误解 一个是catch的使用方法是否正确 另外两个是try/catch的性能损失问题

  有些人认为下面代码就是一个catch的错误用法

  

  catch(Exception e) throw e;

  首先说明 这不是一个错误用法 但是通常来讲 我们应该避免这种代码 然后要说明的是 这段代码有一个比较典型的作用就是改变异常出现的位置 也就是可以对某类异常统一在一个位置处理 先看下面代码

  

  public int GetAllCount () try openDB(); int i = ; return i; catch (SqlException sex) throw sex; catch (Exception ex) throw ex; public int GetAllCount() openDB(); // 这里也可能是微软企业类库等 int i = ; return i; private void openDB() conn Open();

  假设我们有一个公用方法叫openDB() 而很多方法中调用它 当数据库打开失败的时候 对于调用GetAllCount方法 异常将定位于 conn Open而如果调用GetAllCount 那么异常定位于throw sex的位置 同时堆栈信息也有所不同 可以更快捷的找到调用方法的位置 也可在此位置进行一些错误恢复处理 尤其是我们编写一些底层类库的时候 比如 Framework类库从不会把异常代码定位到Framework类库内部的某个方法上面 但是需要注意的是我们尽量避免捕获异常而不返回 例如 catch()

  这样的使用就是典型的错误使用了 因为对于Framework来讲 任何时候系统都可能抛出一个StackOverflowException或者OutOfMemoryExcetpion而上面这段代码则隐藏了这些异常 有时候则导致一些严重的问题

  对于异常处理 在性能上有 点注意

  第一点 在使用try/catch时 如果不发生异常 那么几乎可以忽略性能的损失

  关于这一点 这里我们进行一些深入分析 对此比较了解的可以跳过本节 首先 让我们先看一下try/catch的IL表现 我们有 个方法 一个使用try/catch 而另一个未做任何处理

  

  static int Test (int a int b) try if (a > b) return a; return b; catch return ; static int Test (int a int b) if (a > b) return a; return b;

  使用ILDasm工具查看 IL代码分别如下 (这里之所以引入IL 是因为IL是比较接近机器汇编 所以在IL中我们可以更清楚的了解代码的执行情况 对IL没有兴趣的可以跳过此节)

  

  thod private hidebysig static int Test (int a int b) cil managed // 代码大小 ( x e) maxstack locals init ([ ] int CS$ $ [ ] bool CS$ $ ) IL_ : nop try IL_ : nop IL_ : ldarg IL_ : ldarg IL_ : cgt IL_ : ldc i IL_ : ceq IL_ : stloc IL_ a: ldloc IL_ b: brtrue s IL_ IL_ d: ldarg IL_ e: stloc IL_ f: leave s IL_ b IL_ : ldarg IL_ : stloc IL_ : leave s IL_ b // end try catch [mscorlib]System Object IL_ : pop IL_ : nop IL_ : ldc i m IL_ : stloc IL_ : leave s IL_ b // end handler IL_ b: nop IL_ c: ldloc IL_ d: ret // end of method Program::Test Test thod private hidebysig static int Test (int a int b) cil managed // 代码大小 ( x ) maxstack locals init ([ ] int CS$ $ [ ] bool CS$ $ ) IL_ : nop IL_ : ldarg IL_ : ldarg IL_ : cgt IL_ : ldc i IL_ : ceq IL_ : stloc IL_ : ldloc IL_ a: brtrue s IL_ IL_ c: ldarg IL_ d: stloc IL_ e: br s IL_ IL_ : ldarg IL_ : stloc IL_ : br s IL_ IL_ : ldloc IL_ : ret // end of method Program::Test

  这里我们只需关注红字高亮的几行即可 此处我们只关心try区块 即未发生异常的时候 对于Test 来讲 IL代码多出了 个字节来保存catch的处理代码 这一点对性能和资源几乎是微不足道的

  我们看到当Test 执行到IL_ f或者IL_ 的时候 将数据出栈并使用leave s退出try区块转向IL_ b地址 然后将数据入栈并返回

  对于Test 来讲 执行到IL_ e或者IL_ 的时候 直接退出 并将数据入栈然后返回

  这里对几个关键指令简单介绍一下

  

  nop do noting stloc Pop value from stack into local variable ldloc Load local variable onto stack br s target branch to target short form leave s target Exit a protected region of code short form

  下面我们看代码的实际运行情况 新建一个控制台Console程序 加入下面代码

  

  static void Main(string[] args) int times = ; //我们将结果放大 倍 long l l l l s s ; Console WriteLine( Press any key to continue ); Console Read(); for (int j = ; j < ; j++) l = DateTime Now Ticks; for (int i = ; i < times; i++) Test ( ); l = DateTime Now Ticks; s = l l ; Console WriteLine( time spent: + s ); l = DateTime Now Ticks; for (int i = ; i < times; i++) Test ( ); l = DateTime Now Ticks; s = l l ; Console WriteLine( time spent: + s ); Console WriteLine( difference: +(s s )+ rate: +(float)(s s )/s /times); static int Test (int a int b) try for (int i = ; i < ; i++) ; // 模拟长时操纵 if (a > b) return a; return b; catch return ; static int Test (int a int b) for (int i = ; i < ; i++) ; // 模拟长时操纵 if (a > b) return a; return b;

  运行后可以看到代码的差异 通常在 %的差别以内

  第二点 如果发生异常 那么引发或处理异常时 将使用大量的系统资源和执行时间 引发异常只是为了处理确实异常的情况 而不是为了处理可预知的事件或流控制 例如 如果方法参数无效 而应用程序需要使用有效的参数调用方法 则可以引发异常 无效的方法参数意味着出现了异常情况 相反 用户偶尔会输入无效数据 这是可以预见的 因此如果用户输入无效 则不要引发异常 在这种情况下 请提供重试机制以便用户输入有效输入

  我们经常需要将一个字符串转换为int 比如将Request QueryString[ id ]这样的字符串转换为int 在 x时代 我们常使用下列方式

  

  try int id = Int Parse( ); catch()

  这样的后果是如果出现转换异常 你将不得不牺牲大量的系统资源来处理异常 即使你没有编写任何异常处理代码

  当然你也可以编写大量的代码来检测和转换字符串来替代try/catch方式 而从 以后 框架将这个检测转换过程封装到Int TryParse方法中 再也不用蹩脚的try/catch来处理了

  还要补充一点 就是finally中的代码是始终保证运行的 所以留给大家一个问题 下面代码执行后a的值是多少

  

  int a = ; try int i = Int Parse( s ); catch a = ; return; finally a = ;

cha138/Article/program/net/201311/13168

相关参考