Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is difference between Convert.ToInt16 and (Int16)

I had following piece of code

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();
}

on the basis of which I have lots of questions..
1) Is Convert.ToInt16() and (Int16) both are Unboxing operations
2) If both are related to Unboxing then why they are different. Because the above code shows the following error when Console.WriteLine("{0}", (Int16)s); line is complied
Error:
Specified cast is not valid
3) As i know (Int16) is conventional casting and Convert.ToInt16() is Type safe conversion. But what makes these different here? Maybe it's a silly question but I'm confused. Please clarify this for me and correct me where I'm wrong.

like image 227
Amit Bisht Avatar asked Jun 04 '14 02:06

Amit Bisht


2 Answers

The numeric literal 10 is treated as an integer, and more specifically an Int32. Though you typed your variable as object, under the covers it is still the integer. You can only unbox a value type to its same type, or to a nullable version of that type, directly.

For example, this code:

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

Will not execute, because the original value of i is not a short, it is an integer. You have to first unbox to integer, then you can cast to short.

short j = (short)(int)o;

Convert.ToInt16 sidesteps that issue, and the way it does it is an implementation detail. However, that method has multiple overloads that accepts multiple types, including strings, so it is not the equivalent of code using a direct cast.


Edit: I noticed I'm mixing terms here, so just so it's clear for a novice C# reader, the names short and Int16 are interchangeable for a 16 bit integer, as are the names int and Int32 for 32 bit integers. In C#, short and int are aliases for the .NET types Int16 and Int32, respectively.

like image 131
Anthony Pegram Avatar answered Sep 17 '22 23:09

Anthony Pegram


Your code is violating a C# language rule. Chapter 4.3.2 of the C# Language Specification says:

For an unboxing conversion to a given non-nullable-value-type to succeed at run-time, the value of the source operand must be a reference to a boxed value of that non-nullable-value-type. If the source operand is null, a System.NullReferenceException is thrown. If the source operand is a reference to an incompatible object, a System.InvalidCastException is thrown.

Perhaps not the most clearest possible language, "incompatible object" doesn't help much when talking about value types, but what it says that you can only unbox to an int. A conversion to Int16 (aka short) is not permitted. This rule is not arbitrary, it allows the jitter to generate very efficient code. It doesn't have to consider conversion so it can directly access the bits in the boxed object. Which can be done entirely inline without any helper method, it takes a dozen machine code instructions. This mattered a great deal back in .NET 1.x where generics were not available yet. So collection classes like ArrayList had to store boxed values. Getting them out of the collection required unboxing.

And yes, the Convert class is the workaround. It takes advantage of the value types implementing the IConvertible interface. So it will end up calling the Int32.ToInt16() method. Which first unboxes to int and then casts to Int16. Not as efficient but no kaboom.

like image 39
Hans Passant Avatar answered Sep 20 '22 23:09

Hans Passant