一个类型转换而引发的三级事件的一些思考

 

  前段时间出了个三级事件,查下来居然是由于一个溢出形成的死循环,在公司出事件仍是挺冒险的一件事,除了大boss要扣钱,还要给web

高层一个合理的解释,若是在小公司干活,可能就算网站宕了一天估计也没事,若是在大点的公司每秒都是银子的流失,也许形成的损失就算框架

咱们白干一二年也抵不了,因此责任心和代码意识真的很重要。性能

     先来看看问题代码,在这里我作了一点点的修改,代码的意思很简单,就是想获取参数num中二进制1的个数。测试

 1         static void Run(long num)
 2         {
 3             int i = 1;
 4 
 5             long num2 = 0;
 6 
 7             List<int> list = new List<int>();
 8 
 9             while ((num2 = (long)Math.Pow(2, i - 1)) <= num)
10             {
11                 if ((num & num2) > 0)
12                 {
13                     list.Add(i);
14                 }
15                 i++;
16             }
17         }

若是这是你写的代码,你能一眼看出来问题在哪吗?咱们知道long是8个字节,也就是64位二进制,又由于二进制位中最高位是符号位,因此当网站

是2的63次方时,显示的就是long的minvalue,因此上面的代码当i=64的时候,又由于强转成long,因此最后的结果变成了long的MinValue,spa

循环下去的话就会在负数的道路上越走越远,而后这个范围溢出并无被CLR采纳,也就没有给咱们抛出OverflowException,悲剧就这样.net

无情的发生了,问题是发生了,可是否能从这个问题上有一些思考,在基元类型的强转中,真的适合用(long),(int)这种强转模式吗?从这个例子code

上咱们看到这个(long)模式的强转根本就不会检测溢出,因此之后在强转中最好就不要用这种模式了,由于在.net框架下强转的方式太多了,在blog

我了解的范围内惟独这种没有溢出检测,可能有些人认为这种转换速度是最快的,可是又有多少人能够信誓旦旦的说个人程序绝对不会有溢出,就接口

算程序有溢所形成行为异常我也会负全责的?

   为了推崇非(long)强转,下面介绍一下其余的强转方式。

 

一:为了更好的理解代码,咱们先来看看原始不检测的模式。

1             long i = long.MaxValue;
2             int j = (int)i;

在IL中咱们能够欣喜的看到这种不检测溢出的强转还有专有的IL指令:conv.i4,他的意思就是:将位于计算堆栈顶部的值转换为 int32。

 

二:checked

    在咱们学C#语言的那天起,咱们就知道有一个checked,他的惟一做用就是检测溢出。若是有则抛出异常,那咱们再看看它和无检测的

方式在IL中有什么不一样。

1             long i = long.MaxValue;
2 
3             checked
4             {
5                 int j = (int)i;
6             }

好家伙,看似大串的代码在IL中竟然也就一个指令,其实也就多了一个ovf,这个我想你也应该清楚,在转换的时候多了一个溢出检测。

若是你从性能上反驳的话,确实这个指令性能必定比无检测的慢,我想作web的应该是慢的能够接受。

 

三:Convet.ToXXX。

这个就是C#给咱们专用封装转换操做的类,这个也是我写这篇博客极力推荐的,既然是我推荐的,那确定是会有检测溢出的,下面咱们来

看看代码和IL。

1             long i = long.MaxValue;
2 
3             int j = Convert.ToInt32(i);

在IL上咱们看到并无什么特殊的转换指令,那判断确定就在Toint32方法里面了,下面的目光转移到它的源代码中去看一看。

从源代码中,咱们发现原来代码如此的简洁,尤为是这个if,若是当时用了这个ToIntXXX,也许这个事件就会在测试环境被拦截了,也许某一

天,这个if就是你的最后一根救命稻草。

 

四:IConvertible接口

   在这个接口中封装了不少类型转换的方法,并且全部的基元类型都实现了它,不过没有意思的是居然又调用了下Convert.ToXXX。。。

1             long i = 1;
2 
3             int j = ((IConvertible)i).ToInt32(null);

从上面的IL上能够看到,竟然有一个box,也难怪IConvertible是引用类型,怎么可能不box呢?这个接口方法在值类型转换场景下不值得提

倡,不方便不说,还有较大的性能损失。

 

好了,总结性的话也来了。

① 无检测代码模式: 很是不提倡,总有一天会害死你了。

② checked: 这种虽然有检测,可是写起来麻烦,固然也能够在vs里面本身去设置全局检测。

③ Convert.Toxxx: 这篇就是为了提倡它而写的,因此这个重要性我就不说了,总有一天会救你于水火之中的。

④ IConvertible: 在值类型的场景下,性能最烂并且还很差coding。

相关文章
相关标签/搜索