Convert.ToInt16和(Int16)有什么区别

我有一段代码

try { object s = new object(); s = 10; Console.WriteLine("{0}", Convert.ToInt16(s)); Console.WriteLine("{0}", (Int16)s); } catch (InvalidCastException ex) { Console.WriteLine(ex.Message); Console.ReadLine(); } 

在此基础上我有很多问题..
1)Convert.ToInt16()和(Int16)都是拆箱操作
2)如果两者都与Unboxing有关,那么为什么它们是不同的。 因为上面的代码在Console.WriteLine("{0}", (Int16)s);时显示以下错误Console.WriteLine("{0}", (Int16)s); 线符合
错误:
指定演员表无效
3)据我所知(Int16)是常规的转换和Convert.ToInt16()是类型安全转换。 但是这些不同之处呢? 也许这是一个愚蠢的问题,但我很困惑。 请为我澄清这一点并纠正我错在哪里。

数字文字10被视为整数,更具体地说是Int32 。 虽然您将变量键入为object ,但它仍然是整数。 您只能直接将值类型拆分为相同类型或该类型的可空版本。

例如,这段代码:

 int i = 10; object o = i; short j = (short)o; 

不会执行,因为i的原始值不是短,它是一个整数。 你必须先将unbox转换为整数,然后才能转换为short。

 short j = (short)(int)o; 

Convert.ToInt16回避了这个问题,它的实现方式是实现细节。 但是,该方法有多个重载,它们接受多种类型,包括字符串,因此它等同于使用直接强制转换的代码。


编辑:我注意到我在这里混合术语,所以对于新手C#阅读器来说很明显,名称shortInt16可以互换16位整数,名称intInt32用于32位整数。 在C#中, shortint分别是.NET类型Int16Int32别名。

您的代码违反了C#语言规则。 C#语言规范的第4.3.2章说:

对于在给定的非可空值类型的取消装箱转换以在运行时成功,源操作数的值必须是对该非可空值类型的装箱值的引用。 如果源操作数为null,则抛出System.NullReferenceException。 如果源操作数是对不兼容对象的引用,则抛出System.InvalidCastException。

也许不是最清晰可能的语言,“不兼容的对象”在谈论价值类型时没什么帮助,但是它说你只能拆箱到一个int 。 不允许转换为Int16(又名为short)。 此规则不是任意的,它允许抖动生成非常有效的代码。 它不必考虑转换,因此它可以直接访问盒装对象中的位。 这可以完全内联而无需任何辅助方法,它需要十几个机器代码指令。 这在.NET 1.x中很重要,其中generics还没有。 所以像ArrayList这样的集合类必须存储盒装值。 将它们从集合中取出需要拆箱。

是的,转换类是解决方法。 它利用了实现IConvertible接口的值类型。 所以最终会调用Int32.ToInt16()方法。 哪个首先取消装入int然后再转换为Int16。 效率不高但没有kaboom。

在问题行中,您试图将不是Int16(可能是Int32?)的东西转换为Int16类型。 如果您将作业更改为此我打赌它将起作用( ideone ):

 object s, s2; s = Convert.ToInt16(10); s2 = 10; Console.WriteLine("Types: {0} and {1}", s.GetType(), s2.GetType()); Console.WriteLine("s as Int16 (works): {0}", (Int16)s); Console.WriteLine("s2 as Int16 (error): {0}", (Int16)s2); 

输出

 Runtime error time: 0.03 memory: 36592 signal:-1 Types: System.Int16 and System.Int32 s as Int16 (works): 10 

从注释中,直接将Int32转换为Int16,因为编译器知道它们可以被转换并自动执行。 当您使用裸露的物体时,虽然它会失明并且不会自动转换。 这将适用于例如:

  Console.WriteLine("s2 as Int16 (works): {0}", (Int16)((Int32)s2)); 

这取决于您的应用程序中的“s”。 当您调用Convert.ToInt16(s)时,您可以安全地尝试将任何对象转换为Int16。 如果先前将“s”声明为对象(对象s),则它可以从对象中取消装箱10。 但是因为它是一个对象,所以你不能将它显式地转换为Int16,因为毕竟你确实将它声明为一个对象。 请参阅以下代码:

 object s; s = new object(); s = 10; Int16 newInt = (Int16)s; // This will throw, because s is actually an object Int16 newInt = Convert.ToInt16(s); // This will not fail, because the convert is actually holding a value of 10. 

解包时的盒装值类型实例,您应该注意以下事项:

  1. 盒装值类型实例包含在null中,抛出NullReferenceExceptionexception。

  2. 如果不需要对象的引用点,则盒装值类型实例将引发InvalidCastExceptionexception。

第二个意味着以下代码无法正常工作:

 s = new object(); s = 10; Console.WriteLine("{0}", Convert.ToInt16(s)); Console.WriteLine("{0}", (Int16)s);//throws an InvalidCastException exception 

逻辑可以获得由盒装Int32引用的s,并将其转换为INT6。 对象的取消装箱操作只能转换为未装箱的值类型,在本例中为Int32。 这是正确的措辞:

以下行解决了您的问题

 Console.WriteLine("{0}", (Int16)(int)s); / / first unboxing of the correct type, and then transition