Chapter 10: Inheritance.
Sometimes, you find yourself having common implementation to some classes under a specific category, and you find yourself rewriting the same code over and over.
inheritance is one of the things that can solve that problem.
imagine in your game you have animals that roar, make pain noises, and then death noise when killed.
Lets say first one is a Dog, second is a cat
using System;
public class Dog
{
public int Health;
public Dog()
{
Health = 10;
}
public void Roar()
{
Console.WriteLine("Woof");
}
public void Pain()
{
Console.WriteLine("Woooif");
}
public void Death()
{
Console.WriteLine("Woo, woooooo.");
}
}
public class Cat
{
public int Health;
public Cat()
{
Health = 4;
}
public void Roar()
{
Console.WriteLine("Meow");
}
public void Pain()
{
Console.WriteLine("Meoooowa?");
}
public void Death()
{
Console.WriteLine("Waieow");
}
}
Ok, that does make sense, somewhat, even if we write the same thing, we still need to change it for every animal, so it makes sense.
but what if we have a CheckForDeath and TakeDamage methods that do the same thing in both places?
using System;
public class Dog
{
public int Health;
public bool Dead;
public Dog()
{
Health = 10;
}
public void Roar()
{
Console.WriteLine("Woof");
}
public void Pain()
{
Console.WriteLine("Woooif");
}
public void Death()
{
Console.WriteLine("Woo, woooooo.");
}
public void TakeDamage(int amount)
{
Health -= amount;
Pain();
}
public void CheckForDeath()
{
if (Health <= 0)
{
Death();
Dead = true;
}
}
}
public class Cat
{
public int Health;
public bool Dead;
public Cat()
{
Health = 4;
}
public void Roar()
{
Console.WriteLine("Meow");
}
public void Pain()
{
Console.WriteLine("Meoooowa?");
}
public void Death()
{
Console.WriteLine("Waieow");
}
public void TakeDamage(int amount)
{
Health -= amount;
Pain();
}
public void CheckForDeath()
{
if (Health <= 0)
{
Death();
Dead = true;
}
}
}
Yeah... If we go on it might become super complex super fast.
Thankfully, though, we have inheritance.
public class Animal
{
public int Health;
public bool Dead;
public virtual void Roar()
{
}
public virtual void Pain()
{
}
public virtual void Death()
{
}
public void TakeDamage(int amount)
{
Health -= amount;
Pain();
}
public void CheckForDeath()
{
if (Health <= 0)
{
Death();
Dead = true;
}
}
}
Ok but that looks like a normal class?
technically, kind of. Inherited classes are kinda template like classes that when you inherit it with another class, it's code gets copyed. All of it, but that's not all.
See the methods marked with virtual? Virtual is a keyword only used with methods that we will use with inherited classes.
See, if we had normal methods for the things like Pain, Death or Roar, we won't be able to customize them for every animal, so we make it empty, and we put virtual, so we can use another keyword called override to implement it. Think about it like telling the compiler we have this method, but maybe a class that inherits this one will change it's body, the block inside.
let's see how.
public class Cat : Animal
{
public Cat()
{
Health = 4;
}
public override void Roar()
{
Console.WriteLine("Meow");
}
public override void Pain()
{
Console.WriteLine("Meoooowa?");
}
public override void Death()
{
Console.WriteLine("Waieow");
}
}
public class Dog : Animal
{
public Dog()
{
Health = 10;
}
public override void Roar()
{
Console.WriteLine("Woof");
}
public override void Pain()
{
Console.WriteLine("Woooif");
}
public override void Death()
{
Console.WriteLine("Woo, woooooo.");
}
}
ok I don't see the point? Isn't it exactly the same?
nope, see here, we only have what differs from animal to animal, their sounds, but all the repeated content, the common functions all animals has, is already implemented i animal.
what happens when you do
public class Dog : Animal
is you basically telling c# I want you to grab all Animal has, and throw it to me.
but there is one tiny problem, remember this part in animal?
public virtual void Roar()
{
}
public virtual void Pain()
{
}
public virtual void Death()
{
}
it's empty, but see, it's really not a problem, because we already solved it.
when we did, override on those 3 functions in 'Dog' and 'Cat', We basically told the compiler.
Listen up, there is a virtual method somewhere, inherited, here, and I want you to replace it with what I'm going to give you right now.
But you'll ask yourself, is that the only thing that we get? If so, that's nothing.
Fortunately, the answer is no, copying data is useful, but there's something that makes it even more useful.
Imagine you have a variable that either expects Dog or Cat, but the problem is you can't give a variable of a Type Dog Cat, Or the opposite.
what you can do though, is give a variable of the Type Animal Either Dog or Cat, because Animal is the thing we needs, something that Roars, Howls or Yowls in pain, takes damage and dies, We don't care which one it is, we only care that it is one.
This works because of what we did above, Animal is like a template, and Dog or Cat changes what the Template does, but for all what C# knows, it's the same thing, but it is not. Because we changed it.
Let's see how this really works. We're gonna use arrays.
Console.WriteLine("How many dogs do you have?");
int dogs = int.Parse(Console.ReadLine());
Console.WriteLine("How many cats do you have?");
int cats = int.Parse(Console.ReadLine());
int totalPets = dogs + cats;
Animal[] pets = new Animal[totalPets];
What we did here, is we asked the user how many dogs and cats they have, then got the total of them combined, and made an array with that length.
Now, let's add all the pets to the array.
Console.WriteLine("How many dogs do you have?");
int dogs = int.Parse(Console.ReadLine());
Console.WriteLine("How many cats do you have?");
int cats = int.Parse(Console.ReadLine());
int totalPets = dogs + cats;
Animal[] pets = new Animal[totalPets];
for (int i = 0; i < pets.Length; i++)
{
if (i < cats)
{
pets[i] = new Cat();
}
else
{
pets[i] = new Dog();
}
}
So, there are a few interesting things going on here.
we need to check, since we did some calculating above, we need to make sure that we properly add dogs and cats where they should be, same amount as the user said
The whole idea of Animal is we needed something that holds shared functions, so we don't duplicate them, so, is there a way we can call those functions? Like TakeDamage?
Yes, we can, in fact, that's the whole point.
Console.WriteLine("How many dogs do you have?");
int dogs = int.Parse(Console.ReadLine());
Console.WriteLine("How many cats do you have?");
int cats = int.Parse(Console.ReadLine());
int totalPets = dogs + cats;
Animal[] pets = new Animal[totalPets];
for (int i = 0; i < pets.Length; i++)
{
if (i < cats)
{
pets[i] = new Cat();
}
else
{
pets[i] = new Dog();
}
}
while (true)
{
string command = Console.ReadLine();
if (command == "quit")
{
break;
}
else if (command == "punch")
{
Console.WriteLine("You punched your animals. You're a monster!");
for (int i = 0; i < pets.Length; i++)
{
pets[i].TakeDamage(1);
pets[i].CheckForDeath();
}
}
}
try it out.
Next: Working with libraries.