As it is not answered yet, I will try to give a good answer as possible.
Take a look at the following program:
class Program
{
static void Main(string[] args)
{
Animal a = new Animal();
Cat c = new Cat();
Animal ac = new Cat();
a.Noise(a);
a.Noise(c);
a.Noise(ac);
c.Noise(a);
c.Noise(c);
c.Noise(ac);
a.Poop();
c.Poop();
ac.Poop();
Console.Read();
}
}
public class Animal
{
public void Noise(Animal a)
{
Console.WriteLine("Animal making noise!");
}
public void Poop()
{
Console.WriteLine("Animal pooping!");
}
}
public class Cat : Animal
{
public void Noise(Cat c)
{
Console.WriteLine("Cat making noise!");
}
public void Noise(Animal c)
{
Console.WriteLine("Animal making noise!");
}
public void Poop()
{
Console.WriteLine("Cat pooping in your shoe!");
}
}
Output:
Animal making noise!
Animal making noise!
Animal making noise!
Animal making noise!
Cat making noise!
Animal making noise!
Animal pooping!
Cat pooping in your shoe!
Animal pooping!
You can see that we create a variable a
of type Animal
. It points to an object of type Animal
. It has static and runtime type Animal
.
Next we create Cat
variable which points to a Cat
object. The third object is the tricky part. We create an Animal
variable, which has runtime type Cat
, but static type Animal
. Why is this important? Because at compiletime your compiler knows that the variable ac
is in fact of type Animal
. No doubt about it. So it will be able to do all the stuff that an Animal
object can do.
However, at runtime the object inside the variable is known to be a Cat
.
To demonstrate I created 9 function calls.
First, we pass the objects to an instance of Animal
. This object has a method that takes Animal
objects.
This means that inside Noise()
we can make use of all the methods and fields that an Animal
class has. Nothing else. So if Cat
would have a method Miauw()
, we wouldn't be able to call it without casting our animal a Cat
. (Typecasting is dirty, try to avoid it). So when we execute these 3 function calls we will print Animal making noise!
three times. Clearly. So what does my static type matter then?
Well, we'll get there in a second.
The next three function calls are methods inside the Cat
object. The Cat
object has two methods Noise()
. One takes an Animal
, and the other one takes a Cat
.
So first we pass it a regular Animal
. The runtime will have a look at all the methods and see it has a method Noise
that takes an Animal
. Exactly what we need! So we execute that one and we print Animal
making noise.
The next call passes a Cat
variable which contains a Cat
object. Again, the runtime will have a look. Do we have a method that takes a Cat
, because that is the type of my variable. Yes, yes we do. So we execute the method and we print "Cat making noise".
The third call, we have our variable ac
, which is of type Animal
, but points to an object of type Cat
. We will have a look and see if we can find a method that suits our needs. We take a look at the static type (i.e., type of the variable) and we see that is of type Animal
, so we call the method which has Animal
as parameter.
That is a subtle difference between the two.
Next, the pooping.
All animals poop. However, a Cat
poops in your shoe. So we override the method of our base class and implement it so that the Cat
poops in your shoe.
You will notice that when we call Poop()
on our Animal we get the expected result. The same goes for the Cat c
. However, when we call the Poop
method on ac
, we see that it's an Animal
pooping and your shoe is clean. This is because again, the compiler said the type of our variable ac
is Animal
, you said so. So therefore, it will call the method in the type Animal
.
I hope this is clear enough for you.
Edit:
I keep this in mind by thinking about it this way: Cat x;
is a box which has type Cat
. The box doesn't contain a cat, however, it is of type Cat
. This means that the box has a type, regardless of it's contents. Now when I store a cat inside it: x = new Cat();
, I have put an object inside it of type Cat
. So I put a Cat in a Cat-box. However, When I create a box Animal x;
I can store animals in this box. So when I put a Cat
inside this box, that's okay, because it's an animal. So x = new Cat()
stores a Cat inside an Animal box, which is okay.