C# – 装箱/拆箱/类型转换的问题。 我不明白

我很难理解这一点。 请考虑以下示例:

protected void Page_Load(object sender, EventArgs e) { // No surprise that this works Int16 firstTest = Convert.ToInt16(0); int firstTest2 = (int)firstTest; // This also works object secondTest = 0; int secondTest2 = (int)secondTest; // But this fails! object thirdTest = Convert.ToInt16(0); int thirdtest2 = (int)thirdTest; // It blows up on this line. } 

我在运行时获得的具体错误是Specified cast is not valid. 如果我在Visual Studio中使用QuickWatch (int)thirdTest ,我得到的值Cannot unbox 'thirdTest' as a 'int'

到底发生了什么事?

拆箱检查确切类型,如文档中所述。

取消装箱是从类型对象到值类型或从接口类型到实现接口的值类型的显式转换。 拆箱操作包括:

  • 检查对象实例以确保它是给定值类型的盒装值。

  • 将实例中的值复制到value-type变量中。

如您所见,第一步是检查对象实例是否与目标类型匹配。

同样引用文档:

要使值类型的取消装箱在运行时成功,要取消装箱的项必须是对先前通过装箱该值类型的实例创建的对象的引用。 尝试取消设置null会导致NullReferenceException。 尝试取消对不兼容的值类型的引用的unbox会导致InvalidCastException。

因此,要修复此错误,请确保在尝试取消装箱之前类型匹配:

 object thirdTest = Convert.ToInt16(0); short thirdtest2 = (short)thirdTest; 

究竟发生了什么。

在第一种情况下,您有一个简短的,未装箱的,然后您明确地将其转换为int。 这是编译器知道如何操作的有效转换,因此它可以工作。

在第二种情况下,你有一个int,boxed,它们正在分配给一个int。 这是一个整数的简单拆箱,也是有效的,所以它可以工作。

在第三种情况下,您有一个简短的盒装,您试图将其拆箱到一个不短的变量中。 这不是有效的操作:您无法一步完成此操作。 这也不是一个不常见的问题:如果您正在使用包含SMALLINT列的SqlDataReader ,则不能执行以下操作:

  int x = (int)rdr["SmallIntColumn"]; 

以下任何一个都应该适用于您的第三个示例:

  object thirdTest = Convert.ToInt16(0); int thirdTest2 = Convert.ToInt32(thirdTest); int thirdTest3 = (int)(short)thirdTest; 

Int16是一种写Int16的奇特方式; 那里没有装箱/拆箱,只是16位和32位整数之间的普通CLR转换。

允许的第二种情况是相同类型的盒子和取消盒子:值类型int包装在一个object ,然后被解包。

第三种情况尝试将unbox转换为不同类型( int而不是short ),这是不允许的。