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

时间:2014-06-04 02:12:19

标签: c# casting type-conversion

我有一段代码

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);行时显示以下错误 错误:
指定演员无效
3)据我所知,(Int16)是常规投射,Convert.ToInt16()是类型安全转换。但是在这里有什么不同呢? 也许这是一个愚蠢的问题,但我很困惑。请为我澄清一下,并在我错的地方纠正我。

5 个答案:

答案 0 :(得分:9)

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

例如,此代码:

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

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

short j = (short)(int)o;

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


编辑:我注意到我在这里混合了术语,所以对于新手C#阅读器来说,这是明确的,名称short和{{1}对于16位整数是可互换的,对于32位整数,名称Int16int也是可以互换的。在C#中,Int32short分别是.NET类型intInt16的别名。

答案 1 :(得分:4)

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

  

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

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

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

答案 2 :(得分:1)

在问题行中,您试图将非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));

答案 3 :(得分:0)

这取决于您的应用程序中的“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.

答案 4 :(得分:0)

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

  1. 盒装值类型实例中包含null,抛出NullReferenceException异常。

  2. 如果不需要对象的引用点,则盒装值类型实例会抛出InvalidCastException异常。

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

    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