最近项目开发中遇到了两个bug,第一个问题是策划配置数据表的string字段列,默认没有配置数据,导表之后生成string为空,在程序使用此字段转换为数字的时候出现报错。第二个问题是服务器发过来一个int类型的数值,然后在使用事件分发的时候,出现了装箱和拆箱操作,在进行强转uint类型的时候出现报错,报错的堆栈指向的是事件分发器里面的逻辑代码,通过层层排查最终发现竟然是拆箱的时候出错了,坑啊。

下图就是拆箱出错的测试用例:




python中decimal类型转换成int_拆箱


其中1、2、3的转换操作都是正确的,第4种转换在手机APP上面竟然会出现crash。

对于上面出现的问题进行反思和总结如下:

一、装箱和拆箱

装箱:把值类型转成引用类型
拆箱:把引用类型转成值类型

从定义中可以看出装箱和拆箱操作只可能发生在值类型之间的转换,引用类型之间是不会出现装箱和拆箱操作的。

装箱:object obj=1;

上述代码即为一次装箱过程。系统首先会建立一个索引为[0] object类型的局部变量,将整形数1从栈中取出放在栈顶,执行box指令并在托管堆上申请System.Int32所需内存空间。将栈上的变量弹出存储到索引为0的局部变量中。此操作不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这肯定是要消耗内存和cpu资源的。

拆箱:int x=(int)obj;

上述代码即为一次拆箱过程。系统首先会建立一个索引为[1]的int32类型局部变量,将堆上索引为[0]的变量值取出压入栈顶,执行unbox.any指令,生成System.Int32地址指针并读取数据存入栈。后将栈上的变量存储到索引为[1]的int32类型变量中。拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并给值类型变量。

由于装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以项目开发中尽量使用泛型定义来避免装箱和拆箱的出现。

二、数值类型之间的转换--(int)和 int.Parse()和int.TryParse()和Convert.ToInt32()


private


对上面的测试代码进行总结:

1、null 空类型的转换:int.Parse()和直接强转是会报错的,其他转换返回0

2、number 数字类型的转换:Convert.ToInt32()是参与四舍五入取值,强转是直接向下取整。对于大值转向小值,Convert.ToInt32()和int.Parse()都会报错,

虽然强转不会报错,但是精度不对,会对原数据进行截取,结果也是错误的。

3、string 字符串类型的转换:对于空字符和浮点数的string的转换int,只有int.TryParse()不会报错,其他都会报错。策划配置表经常出现类似的配置问题,需要注意。

综上,我们对数据转换要特别谨慎,选择合适的函数,才能保证不会出错。建议如果是数值类型之间的转换直接可以强转。如果是字符串类型的转换建议int.Parse()和int.TryParse() 如果是装拆箱建议用Convert.ToInt32()。实在不知道选择哪个类型建议用int.TryParse(),做好对转换返回值bool的判断就可以了。

by 2020-06-15 周日 凌晨