我有一段代码
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()
是类型安全转换。但是在这里有什么不同呢?
也许这是一个愚蠢的问题,但我很困惑。请为我澄清一下,并在我错的地方纠正我。
答案 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位整数,名称Int16
和int
也是可以互换的。在C#中,Int32
和short
分别是.NET类型int
和Int16
的别名。
答案 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)
解包期间的盒装值类型实例,您应注意以下事项:
盒装值类型实例中包含null,抛出NullReferenceException异常。
如果不需要对象的引用点,则盒装值类型实例会抛出InvalidCastException异常。
第二个意味着以下代码无法正常工作:
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