18

I'm somewhat new to programming and I have a question about classes, inheritance, and polymorphism in C#. While learning about these topics, occasionally I'll come across code that looks something like this:

Animal fluffy = new Cat();  // where Animal is a superclass of Cat*

This confuses me, because I don't understand why someone would create a variable of type Animal to store an object of type Cat. Why wouldn't a person simply write this:

Cat fluffy = new Cat();

I do understand why it's legal to store a child object in a parent type variable, but not why it's useful. Is there ever a good reason to store a Cat object in an Animal variable vs. a Cat variable? Can a person give me an example? I'm sure it has something to do with polymorphism and method overriding (and/or method hiding) but I can't seem to wrap my head around it. Thanks in advance!

Sayse
  • 42,633
  • 14
  • 77
  • 146
dhughes01
  • 205
  • 1
  • 2
  • 3
  • `Animal fluffy = new Cat();` will create an object with *static* type `Animal` and *dynamic* type `Cat`. You should experiment for yourself with methods that take `Animal` and methods that take `Cat` and pass it through them. For example, give both classes a method `.makeNoise()` and then create a method `MakeNoise(Cat c)` and `MakeNoise(Animal a)`. The differences are subtle, but knowing about them makes you a better programmer. – Christophe De Troyer Apr 19 '14 at 16:29
  • 2
    @ChristopheDeTroyer - No. The stament creates 1 instance of type Cat and 1 reference of type Animal. Both are statically typed, no dynamics here. The jargon is pretty hard to get right, for such a basic statement. – H H Apr 19 '14 at 17:55
  • Yes indeed, I meant runtime type. My apologies. :) – Christophe De Troyer Apr 19 '14 at 17:58
  • 1
    possible duplicate of [What does "program to interfaces, not implementations" mean?](http://stackoverflow.com/questions/2697783/what-does-program-to-interfaces-not-implementations-mean) – Jeroen Vannevel Apr 19 '14 at 22:05
  • 1
    If you are comming from a language like C++, this could be confusing:In OO languages like Java or C#, your variable doesn't hold the value itself, instead your variable is a pointer to the object which holds (represents) the value. So your variables are pointers, allowing us to point to instances of derived classes in a polymorphic way. Even if many people think about Java saying "No pointers, hooray!" **the first thing you have to understand in this languages is that everything is a pointer** – Manu343726 Apr 20 '14 at 10:28
  • @Manu343726 All reference types are. Integers etc are still not pointers in C# and/or Java. More info here: http://stackoverflow.com/questions/5201329/does-java-make-distinction-between-value-type-and-reference-type – Christophe De Troyer Apr 20 '14 at 11:27
  • @Christophe I know, but the question talks about polymorphism and inheritance, in other words, reference types – Manu343726 Apr 20 '14 at 12:09

9 Answers9

19

The shortest example I can give you is if you want a list of all animals

 List<Animal> Animals = new List<Animal>();
 Animals.Add(new Cat());
 Animals.Add(new Dog());

If you have ever created a project using Winforms, you will have already used something similar since all controls derive from Control. You will then notice that a Window has a list of controls (this.Controls), that allows you to access all child controls on a window at once. I.E to hide all controls.

 foreach(var control in this.Controls)
      control.Hide();
Sayse
  • 42,633
  • 14
  • 77
  • 146
14

but not why it's useful.

Take a look at some better examples:

Cat myCat = new Cat();
Dog myDog = new Dog();

List<Animal> zoo = ...;  // A list of Animal references
zoo.Add(myCat);          // implicit conversion of Cat reference to Animal reference
zoo.Add(myDog);

and

void CareFor(Animal animal) { ... }
CareFor(myCat);         // implicit conversion of Cat reference to Animal reference
CareFor(myDog);

The pattern Animal fluffy = new Cat(); is far less common in real code (but it does occur).

Consider that very simplified code that shows how some feature works is anot always good at demonstrating the why of that feature.

H H
  • 263,252
  • 30
  • 330
  • 514
6

Let's see a practical but extreme exemple.

class Animal { }
class Bird : Animal { }
class Cat : Animal { }
class Dog : Animal { }
class Elephant : Animal { }
class Fennec : Animal { }

Let's say we have a Person class. How do we store a reference to his lone and unique pet ?


Method 1 : the insane way

class Person
{
    public Bird myBird;
    public Cat myCat;
    public Dog myDog;
    public Elephant myElephant;
    public Fennec myFennec;
}

In that mess, how do we retrieve the pet ?

   if (myBird != null)
    {
        return myBird;
    }
    else if (myCat != null)
    {
        return myCat;
    }
    else if (myDog != null)
    {
        return myDog;
    }
    else if (myElephant != null)
    {
        return myElephant;
    }
    else if (myFennec != null)
    {
        return myFennec;
    }
    else
    {
        return null;
    }

And I'm being nice here, with only 5 types of Animal. Let's say we have over 1000 types of Animal. Will you be the one to write all those variables in the Person class, and add all those 'else if ()' in every place they are, in your application ?


Method 2 : a better approach

class Person
{
    public Animal myPet;
}

That way, thanks to polymorphism, we have our lone and unique reference to the person's pet, and in order to get the pet, we simply write :

return myPet;

So, what is the best way of doings things ? Method 1 or 2 ?

David Khuu
  • 937
  • 3
  • 10
  • 21
  • You could make the examples runnable with very little effort. – Ilmo Euro Apr 21 '14 at 08:29
  • @IlmoEuro If you are talking about the code blocks made of if-else-return, they were all initially in a 'Animal GetPet()' function, but I felt it wouldn't add much to show the method definition. In addition, the initial question was about the usefulness of storing a reference of an object of type Y in a variable or field of type X, where Y is derived from X. So, I scrapped the method definitions, and focused on an exemple only related to variables or fields. – David Khuu Apr 21 '14 at 15:04
3

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.

Christophe De Troyer
  • 2,852
  • 3
  • 30
  • 47
  • 1
    Wrong on several points. You are using a non-standard and confusing jargon. Objects don't have 'runtime' or 'static' types, they just have 1 type. Polymorphism comes from pointing to them with references of different (but related) types. – H H Apr 20 '14 at 08:48
  • No, that is not correct :) Object DO have runtime type. By static type I mean the type of the variable. And by runtime type I mean the type of the object that the variable points to. – Christophe De Troyer Apr 20 '14 at 09:15
  • 1
    You can mean that but it's not the standard nomenclature. And your example with the 'box' is misleading. Polymorphism is in the references, not in the objects. – H H Apr 20 '14 at 14:33
  • You are 100% correct! – RazorAlliance192 Apr 21 '14 at 19:55
3

A declaration which includes initialization, such as Animal joesPet = new Cat(), can have two purposes:

  • Create an identifier which will throughout its scope will always represent the same thing.

  • Create a variable which will initially hold one thing, but may later hold something else.

Declarations where a parent-type variable is initialized to refer to a sub-type instance are commonly used for the second purpose, in situations where the variable is initially assigned to an instance of a particular subtype, but may later need to hold references to things that are not of that subtype. If the declaration had been Cat joesPet = new Cat(); or var joesPet = new Cat();, then it would (for better or worse) not be possible to say joesPet = new Dog();. If code shouldn't be able to say joesPet = new Dog();, then the fact that declaring as Cat or var would prevent that would be a good thing. On the other hand, if code might need to have joesPet be something other than a Cat, then it should declare the variable in such a way as to allow that.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

The reason is Polymorphism.

Animal A = new Cat();
Animal B = new Dog();

If Func takes an Animal and Animal implements MakeNoise():

Func(A);
Func(B);


...

void Func(Animal a)
{
    a.MakeNoise();
}
Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
1

Easy answer: If you use the interface or base class animal you can write generic methods that that can take all types of animals instead of only one.

See Why use an interface when the class can directly implement the functions .

Community
  • 1
  • 1
user2609980
  • 10,264
  • 15
  • 74
  • 143
1

I use that pattern a few times too, in a somewhat more advanced context but maybe it's worth mentioning. When writing unit tests exercising a service/repository or any class that implements an interface, I often type its variable with the interface instead of the concrete type:

IRepository repository = new Repository();
repository.Something();
Assert.AreEquals(......);

I see this particular case a better option to have the variable as the interface type because it helps as an additional check that the interface is in fact properly implemented. As most likely in real code I won't be using the concrete class directly, I find better to have this little extra verification.

Alejandro
  • 7,290
  • 4
  • 34
  • 59
0

If you're writing a program that emulates the behavior of animals, all animals have things in common. They walk, they eat, they breath, they eliminate, etc. What they eat and how they walk, among other things, are different.

So your program knows that all animals do some things, so you write a base class called Animal that does all those things. The things that all animals do the same (breath, eliminate) you can program in the base class. Then, in the subclasses, you write the code that handles the things they all do but do differently from other animals, like what they eat and how they walk.

But the logic that controls how each animal behaves doesn't care about the details of how they do anything. The "brain" of the animal just knows that it's time to eat, walk, breath, or eliminate. So it calls the method that does those things on a variable of type Animal, which ends up calling the correct method depending on what the actual type of Animal the object it's referring to is.

Tony Vitabile
  • 8,298
  • 15
  • 67
  • 123