Introduction
I briefly mentioned the data types int (aka Int32) and long (aka Int64) in a previous blog Integer Overflow â Zaccaria Pinball. A key point is that the maximum value of an âInt32â type is 2,147,483,647, whereas âInt64â has a max of 9,223,372,036,854,775,807.
This blog is going to be more nerdy than my usual ramblings. It’s going to cover some C# code. In this blog, I’m going to show some interesting things that happen with Casting. Casting is defined by
A cast is a way of explicitly informing the compiler that you intend to make the conversion and that you are aware that data loss might occur, or the cast may fail at runtime.
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions
Simple Casts
Casting is done by putting the datatype in brackets next to the variable. Let’s see that in action.
We can begin with an Int32 (I’ll call my variable “anInt32”). Then on the next line, I will cast it to an Int64.
Int32 anInt32 = 50;
Int64 anInt64 = (Int64)anInt32;
We can also start with an Int64 (I’ll call it “anInt64”). On the next line, I will cast to an Int32
Int64 anInt64 = 50;
Int32 anInt32 = (Int32)anInt64;
The number 50 can fit in either Int32 or Int64. This works just as I intended. Nothing bad happens. But let’s move onto the more interesting scenarios.
Downcasting – Dataloss
So let’s create a large number and correctly store it in a Int64. Then we try and down-cast it into an Int32. It’s isn’t going to fit, so what happens to the number?
Int64 largeInt64 = 19992244551234;
Int32 intNumber = (Int32)largeInt64;
It overflows. The number stored in the variable “intNumber” becomes -828211646 and we have a bug. This is basically what happened in the Zaccaria Pinball example. The number was simply too big to fit in the space that an Int32 allows. If you are playing a computer game, it’s better than out-right crashing, I guess.
Casting With Objects
The previous examples show you can convert between Int32 and Int64 by casting.
Instead of being very specific with your variables, you could declare variables as the base type “Object”. Despite Objects being set to a number type such as Int32 or Int64, you can only successfully cast when the type matches, otherwise it will give you a runtime error, and say the “System.InvalidCastException: Specified cast is not valid.“. It’s quite a hard thing to explain, but let’s see it in action.
We first declare a variable of type “Object”. It is implicitly an Int32. Then on the next line, we cast it to Int64. In previous examples this would be fine, but as I try to explain in the previous paragraph, declaring it as an Object seems to play by different rules. Therefore the following code throws the InvalidCastException, since the declaration was implicitly an Int32.
Object intObject = 30024;
Int64 int64cast = (Int64)intObject;
So if we declare it explicitly as an Int64, then casting from Object to Int64 does work because the underlying type was an Int64:
Object int64Object = 30024L; //explicitly an Int64 by using the L character
Int64 int64cast = (Int64)int64Object;
But, similarly, as we saw in the previous failed example, casting it to an Int32 results in InvalidCastException, because the type does not match.
Object int64Object = 30024L; //explicitly an Int64 by using the L character
Int32 intcast = (Int32)int64Object;
Very frustrating and quite confusing! Earlier in the blog, I was showing you that if the original types were declared as a proper number types (Int32 and Int64), we can cast between them. However, when it was originally declared simply as an Object, we can’t seem to do this.
Convert to the rescue
Instead of casting, we can use Convert. These examples work as intended.
Object intObject = 30024;
Int64 int64convert = Convert.ToInt64(intObject);
Object int64Object = 30024;
int intconvert = Convert.ToInt32(int64Object);
Try It Yourself
Here is the full code to easily paste into Visual Studio (or your favourite IDE) and try it yourself.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Playground
{
class CastingNonsense
{
internal static void Run()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
}
private static void Test1()
{
Int32 anInt32 = 50;
Int64 anInt64 = (Int64)anInt32;
//fine
}
private static void Test2()
{
Int64 anInt64 = 50;
Int32 anInt32 = (Int32)anInt64;
//fine
}
private static void Test3()
{
Int64 largeInt64 = 19992244551234;
Int32 intNumber = (Int32)largeInt64;
//overflows, but doesn't crash. Number becomes -828211646
}
private static void Test4()
{
Object intObject = 30024;
Int64 int64cast = (Int64)intObject; //InvalidCastException
}
private static void Test5()
{
Object int64Object = 30024L; //explicitly an Int64 by using the L character
Int64 int64cast = (Int64)int64Object;
}
private static void Test6()
{
Object int64Object = 30024L; //explicitly an Int64 by using the L character
Int32 intcast = (Int32)int64Object;
}
private static void Test7()
{
//Convert solves the problem of casting. This works fine
Object intObject = 30024;
Int64 int64convert = Convert.ToInt64(intObject);
Object int64Object = 30024;
int intconvert = Convert.ToInt32(int64Object);
}
}
}