C#: Casting and Type Checking
We all know and have experienced the importance we have for castings in C#. Its just essential if you want to build any system that takes inputs, process and give output. What a basic computer does is the same functionality right? so logically we need casting EVERYWHERE in computing. Even you can argue, "I am a C# programmer, I write a console application that takes two integers, calculate their sum of it and print the output, so where I have to cast here?" my answer is "CLR does this".
Casts vs Conversion
In simple and understandable words, "casts are basically explicit conversion". In internal conversions we can pretty much ensure on three things
- No special syntax required
- They are type safe
- No data-loss
Normally we can take conversions from smaller to larger integral types and conversions from derived classes to base classes as good examples for this type of internal conversions. Explicit conversions, require a cast operator. We have to explicitly say the compiler to forcefully convert the type. So there might be a very possible data-loss.
double pi = 22/7;
int a = (int)pi;
(Un)Boxing vs Casting
Honestly (Un)Boxing is a subset of casting. Boxing means CLI consider a value type as reference type (that involves copying the contents of that particular value type which is on stack to the heap and returning a reference to the particular object). This lets a value type to be passed wherever a compatible reference type is expected and also this allows virtual method calls and some other features of reference types to be processed on the value type. Unboxing is the reverse of this boxing operation (getting back a value type out of a boxed object). Casting is taking a type (say, System.Int32) and consider it as another type (say, System.Double). When you box something in C#, you are casting it to another type. *The difference is that it allocates additional memory as a new reference type is created.
*
// just a variable
int number = 0;
// boxing it
object boxed_number = number;
// unboxing (avtually a type casting)
int unboxed_number = (int)boxed_number;
as and is
as
and is
are used in C# to safely cast things. Since C# objects are polymorphic its possible that a base class type variable can hold a derived type so to access the derived type's method, it is necessary to cast the value back to the derived type. However this has a risk of InvalidCastException
. This is where as
and is
comes to the play. is
is used to check the compatibility of two objects with a given type and returns true
or false
. While as
can used only with reference type and will return null
if casting fails. basically
Animal s = cat as Animal;
//above is equivalent to below
Animal s = cat is Animal ? (Animal)obj : (Animal)null;
as Makes Readable Code
Think of the below code piece,
(Bat as Mammal).Walk();
Simply you can argue, this is a confusing and a useless way since we are using as
keyword we are not sure about if BAT
is a type of MAMMAL
and yet we are invoking a method. And even worse, this code piece can make a meaningful InvalidCastException
to a NullReferenceException
we all hate it. But why in some places they have mentioned this kind of a code piece? this is where code readability comes to mind. We know if we are using as
, we are sure about this object can be converted to that type but if it can't we are happy to accept a null
. However as I said before as
can only be used with reference types, by using this style of coding, we can tell code reader that the code involves only reference or unboxing conversions, and furthermore, that BAT is not null. typeof
vs is
typeof
and is
does almost the same but by stating is
we can say it can be a derived type or from interface. So again using is
in necessary places ensures code readability and sometimes. Still to use typeof
properly we have to use GetType()
if (obj1.GetType() == typeof(int));
if (obj1 is int);
typeof() vs GetType()
Even though GetType()
can be used closely to work with typeof
there is a slight different. typeof
is used when you want to get the Type instance representing a specific type. GetType()
gives the runtime type of the object on which it is called, which may be different from the declared type. The below example make a clarification on it.
class A {}
class B : A {}
class Program
{
static A CreateA()
{
return new B();
}
static void Main()
{
A a = CreateA();
Console.WriteLine(typeof(A)); // Writes "A"
Console.WriteLine(a.GetType()); // Writes "B"
}
}
Always remember, GetType actually queries a specific assembly (at runtime) for a type that might be defined within the assembly (Similar to new Object().GetType()). typeof on the other hand is determined at compile time.
Using GetType() in some instances might lead us to a null related exception, since this function has been defined something like below
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType();
So when we call this, this will only check from the calling assembly and some few other system assemblies (probably System and mscorlib). It does not check every assembly. So the string parameter inside GetType() sometimes needed to be a fully-qualified assembly name. Thus in performance, typeof() takes little time than GetType() to get executed. Consider the example below
string name = typeof(Form).AssemblyQualifiedName;
Console.WriteLine(name);
Type type = Type.GetType(name);
Console.WriteLine(type);
This will return the following output
System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Windows.Forms.Form
I hope this will make more sense when you try to understand the above scenario.