指定的转换无效. (sqlmanagerui)_“System.InvalidCastException 指定的转换无效”问题的反思和总结...

本文针对在使用SqlManagerUI时遇到的'System.InvalidCastException'问题进行分析,主要讨论了装箱和拆箱的概念,以及数值类型间的转换方法,包括(int)、int.Parse()、int.TryParse()和Convert.ToInt32()。总结了各种转换在处理不同输入(如null、数字、字符串)时的行为,并提出了在项目开发中减少装箱拆箱和选择合适转换方法的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

f38629e0adf57a59848ded962daf6ec2.png

其中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 void ConvertType()
{
    ////////////////////////////////////////////////////////////////null type
    int val1 = Convert.ToInt32(null); //0
    //int val2 = int.Parse(null); //Error: System.ArgumentNullException:“值不能为 null。Arg_ParamName_Name”

    int val3; //0
    bool bVal3 = int.TryParse(null, out val3);//false
    //int val4 = (int)null; //Error: 无法将 null 转换为int,因为后者是不可以为null的值类型

    ////////////////////////////////////////////////////////////////number type
    float f = 1.1111f;
    int val5 = Convert.ToInt32(f);//1  //采取的取舍是进行四舍五入
    //int val6 = int.Parse(f.ToString());//Error: System.FormatException:“输入字符串的格式不正确。”
    int val7;//0
    bool bVal7 = int.TryParse(f.ToString(), out val7);//false
    int val8 = (int)f;//1   //截取浮点型的整数部分,忽略小数部分。即向下取整

    long l = 9111111111111111111;
    //int val9 = Convert.ToInt32(l);//Error; System.OverflowException:“值对于 Int32 太大或太小。”
    //int val10 = int.Parse(l.ToString());//Error: System.OverflowException:“值对于 Int32 太大或太小。”
    int val11;//0
    bool bVal11 = int.TryParse(l.ToString(), out val11);//false
    int val12 = (int)l;//1726247367

    ////////////////////////////////////////////////////////////////string type
    string s1 = "";
    //int val13 = Convert.ToInt32(s1);//Error: System.FormatException:“输入字符串的格式不正确。”
    //int val14 = int.Parse(s1);//Error: System.FormatException:“输入字符串的格式不正确。”
    int val15;//0
    bool bVal15 = int.TryParse(s1, out val15);//false
    //int val16 = (int)s1;//Error: 无法将string转换为int

    string s2 = "1.111";
    //int val17 = Convert.ToInt32(s2);//Error: System.FormatException:“输入字符串的格式不正确。”
    //int val18 = int.Parse(s2);//Error: System.FormatException:“输入字符串的格式不正确。”
    int val19;//0
    bool bVal19 = int.TryParse(s2, out val19);//false
    //int val20 = (int)s1;//Error: 无法将string转换为int

    Console.WriteLine("ConvertType Over");
}

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

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 周日 凌晨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值