Throwing an Invalid Cast Exception?... 拆箱引起的一个错误

隐式和显示转换通常都非常简单:

int i = 5;
float f = i; // int32到float的隐式转换
Byte b = (Byte) i; // int32到byte的显示转换

 如果一不留神,安全的显示数值转换会引发一个运行时异常:

Object i = 5;
float l = (float) i; // System.InvalidCastException: Specified cast is not valid.

 

ArrayList a = new ArrayList();
int b;            
for (int i = 0; i < 10; i++) {
    b = 10;  
    a.Add(b);        
}
float f = (float) a[0]; // System.InvalidCastException: Specified cast is not valid.

为什么从 int 转换为 float 是可以的,但从 int ArrayList[i] 转换到 float 就不行了呢?

 

类型转换 vs 数值转换

引发InvalidCastException 的原因并不是因为从 int 到 float 的显式转换。

而是因为我们把一个 int 型的值拆箱成 float。

值类型引用类型的一个重要区别就是它们分配的内存位置不同。

值类型被分配在堆栈空间,引用类型被分配在堆空间

装箱的意思是把值类型转化为引用类型。

下面几个操作就会触发装箱

  • 将值类型赋值给引用类型字段
  • 将值类型添加到引用类型集合中
  • 将值类型作为参数传递给采用引用类型的方法

拆箱的意思是把引用类型转化为值类型。

下面的几个操作会触发拆箱:

  • 从引用类型中提取值类型
  • 从引用类型集合中提取值类型
  • 把引用类型作为参数传递给采用值类型的方法

拆箱的约束:

拆箱时,CLR规定了两个约束:

  1. 如果引用类型的值时null,会引发 NullReferenceException。
  2. 如果引用不指向装箱时期望的值类型,会引发 InvalidCastException。

拆箱到不同于原始装箱值类型的值类型是不允许的。

Object i = 5; // 将整型5装箱
float l = (float)i; // 拆箱成float型
ArrayList a = new ArrayList();
int b;            
for (int i = 0; i < 10; i++) {
    b = 10;  
    a.Add(b);  // 将整型数b装箱到引用类型集合a中     
}
float f = (float) a[0]; // 将整型数b拆箱成float型

现在我们知道了问题所在,那么怎么解决呢?

System.Convert Namespace

如果你看了.NET Reference Source关于 System.Int32 的代码,你会发现它实现了 IConvertible接口。

public struct Int32 : 
    IComparable,
    IFormattable,
    IConvertible, 
    IComparable<Int32>,
    IEquatable<Int32>

像 System.Int32 这样实现了 IConvertible 接口的值类型,可以使用静态方法 System.Convert 来进行拆箱和显示转换 。

下面是 .NET Reference Source中的 System.Convert.ToSingle 的定义:

public static float ToSingle(object value) {
    return value == null? 0: ((IConvertible)value).ToSingle(null);
}

如果我们传递一个装箱过的 int 类型给.ToSingle(object value),这个方法就会调用System.Int32struct中定义的.ToSingle()方法:

/// <internalonly/>
float IConvertible.ToSingle(IFormatProvider provider) {
    return Convert.ToSingle(m_value);
}

下面来看正确的例子:

Object o = 5;
float f = Convert.ToSingle(o);
ArrayList a = new ArrayList();
int b;            
for (int i = 0; i < 10; i++) {
    b = 10;  
    a.Add(b);        
}
Convert.ToSingle(a[0]);

  here有更多示例代码。

总结

当我们试图进行一次显示数值转换时,得到了一个运行时错误。

造成这个错误的真正原因是因为我们把一个值类型转换为引用类型,再把这个引用类型转换回值类型。通常这两种操作被称作装箱拆箱。

拆箱的一个规则是只能拆成最初的值类型。否则,编译器就会报一个InvalidCastException的错误。

我们的解决方案是利用静态类 System.Convert。它提供了.ToXXX 方法,这个方法可以被实现了 IConvertible 接口的值类型调用。

希望这篇文章解决了你的问题,帮助你了解了CLR在屏幕后做了什么。

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值