一个类型转换而引起的三级事件的一些思考 数据检查
2014-09-28 09:09
519 查看
前段时间出了个三级事件,查下来竟然是因为一个溢出造成的死循环,在公司出事件还是挺冒险的一件事,除了大boss要扣钱,还要给
高层一个合理的解释,如果在小公司干活,可能就算网站宕了一天估计也没事,如果在大点的公司每秒都是银子的流失,也许造成的损失就算
我们白干一二年也抵不了,所以责任心和代码意识真的很重要。
先来看看问题代码,在这里我做了一点点的修改,代码的意思很简单,就是想获取参数num中二进制1的个数。
如果这是你写的代码,你能一眼看出来问题在哪吗?我们知道long是8个字节,也就是64位二进制,又因为二进制位中最高位是符号位,所以当
是2的63次方时,显示的就是long的minvalue,所以上面的代码当i=64的时候,又因为强转成long,所以最后的结果变成了long的MinValue,如
果再循环下去的话就会在负数的道路上越走越远,然后这个范围溢出并没有被CLR采纳,也就没有给我们抛出OverflowException,悲剧就这样
无情的发生了,问题是发生了,但是否能从这个问题上有一些思考,在基元类型的强转中,真的适合用(long),(int)这种强转模式吗?从这个例子
上我们看到这个(long)模式的强转根本就不会检测溢出,所以以后在强转中最好就不要用这种模式了,因为在.net框架下强转的方式太多了,在
我了解的范围内唯独这种没有溢出检测,可能有些人认为这种转换速度是最快的,但是又有多少人可以信誓旦旦的说我的程序绝对不会有溢出,就
算程序有溢出所造成行为异常我也会负全责的?
为了推崇非(long)强转,下面介绍一下其他的强转方式。
一:为了更好的理解代码,我们先来看看原始不检测的模式。
在IL中我们可以欣喜的看到这种不检测溢出的强转还有专有的IL指令:conv.i4,他的意思就是:将位于计算堆栈顶部的值转换为 int32。
二:checked
在我们学C#语言的那天起,我们就知道有一个checked,他的唯一作用就是检测溢出。如果有则抛出异常,那我们再看看它和无检测的
方式在IL中有什么不同。
好家伙,看似大串的代码在IL中居然也就一个指令,其实也就多了一个ovf,这个我想你也应该清楚,在转换的时候多了一个溢出检测。
如果你从性能上反驳的话,确实这个指令性能一定比无检测的慢,我想做web的应该是慢的可以接受。
三:Convet.ToXXX。
这个就是C#给我们专用封装转换操作的类,这个也是我写这篇博客极力推荐的,既然是我推荐的,那肯定是会有检测溢出的,下面我们来
看看代码和IL。
在IL上我们看到并没有什么特殊的转换指令,那判断肯定就在Toint32方法里面了,下面的目光转移到它的源代码中去看一看。
从源代码中,我们发现原来代码如此的简洁,尤其是这个if,如果当时用了这个ToIntXXX,也许这个事件就会在测试环境被拦截了,也许某一
天,这个if就是你的最后一根救命稻草。
四:IConvertible接口
在这个接口中封装了很多类型转换的方法,而且所有的基元类型都实现了它,不过没有意思的是竟然又调用了下Convert.ToXXX。。。
从上面的IL上可以看到,居然有一个box,也难怪IConvertible是引用类型,怎么可能不box呢?这个接口方法在值类型转换场景下不值得提
倡,不方便不说,还有较大的性能损失。
好了,总结性的话也来了。
① 无检测代码模式: 非常不提倡,总有一天会害死你了。
② checked: 这种虽然有检测,但是写起来麻烦,当然也可以在vs里面自己去设置全局检测。
③ Convert.Toxxx: 这篇就是为了提倡它而写的,所以这个重要性我就不说了,总有一天会救你于水火之中的。
④ IConvertible: 在值类型的场景下,性能最烂而且还不好coding。
高层一个合理的解释,如果在小公司干活,可能就算网站宕了一天估计也没事,如果在大点的公司每秒都是银子的流失,也许造成的损失就算
我们白干一二年也抵不了,所以责任心和代码意识真的很重要。
先来看看问题代码,在这里我做了一点点的修改,代码的意思很简单,就是想获取参数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,如
果再循环下去的话就会在负数的道路上越走越远,然后这个范围溢出并没有被CLR采纳,也就没有给我们抛出OverflowException,悲剧就这样
无情的发生了,问题是发生了,但是否能从这个问题上有一些思考,在基元类型的强转中,真的适合用(long),(int)这种强转模式吗?从这个例子
上我们看到这个(long)模式的强转根本就不会检测溢出,所以以后在强转中最好就不要用这种模式了,因为在.net框架下强转的方式太多了,在
我了解的范围内唯独这种没有溢出检测,可能有些人认为这种转换速度是最快的,但是又有多少人可以信誓旦旦的说我的程序绝对不会有溢出,就
算程序有溢出所造成行为异常我也会负全责的?
为了推崇非(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。
相关文章推荐
- 一个类型转换而引起的三级事件的一些思考
- 关于C++和C#的自定义数据类型转换的一些思考
- 在做一些复杂的类型转换之前(比如将一个数据转换成一个属性的类型,属性可能为可空类型)先判断该类型是否为可空类型,否则会报如下错误:
- 7.1 数值数据类型检查与转换
- java一些数据类型转换
- 小数点引起的数据类型转换问题
- 从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值。
- 7.4 数据类型检查与转换应用实例
- 一个类型转换引起的Bug
- 如何将一个List<Map<String,String>>类型的集合数据转换成json格式输出
- Entity Framework 更新数据库 datetime 类型的问题 从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值。
- socket通讯中需要用到的一些数据类型转换的方法
- 在C#中将任意数值类型数据与字节数组相互转换的一个方法
- 一个用于网络传输,转换数据类型到byte的例子
- 关于类型原型的思考(请不要在一个Map或List放入多种数据类型)
- 如何将QString转换为binary数据类型!每两个字符转换为一个binary
- 7.2 布尔数据类型检查与转换
- BCB中Byte[]类型转换成AnsiSring类型数据的一个函数
- 对java基础数据类型在运算过程中的自动转换的一些分析
- 小数点引起的数据类型转换问题