Day 11: Inheritance and Polymorphism - When Classes Have Babies
arrow_back All Posts
April 06, 2026 7 min read .NET/C#

Day 11: Inheritance and Polymorphism - When Classes Have Babies

Welcome back! On Day 10 you learned to build your own classes โ€” little self-contained worlds with data and behavior. Today we're taking it further: making new classes based on existing ones. This is inheritance, and its partner in crime, polymorphism, is how you write code that works with objects it hasn't even met yet. Let's go. ๐Ÿงฌ

1. What Is Inheritance?

Imagine you've built a perfectly good Animal class. Now you need a Dog class and a Cat class. Do you rewrite everything from scratch? No โ€” you inherit from Animal and add only what's different.

Think of it like genetics: a child inherits traits from a parent (eye color, height) but also develops their own unique traits (terrible music taste, for example).

class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

class Dog : Animal
{
    public string Breed { get; set; }

    public Dog(string name, string breed) : base(name)
    {
        Breed = breed;
    }

    public void Fetch()
    {
        Console.WriteLine($"{Name} fetches the ball!");
    }
}

The : Animal after Dog means "Dog inherits from Animal." The : base(name) in the constructor calls Animal's constructor first โ€” because a Dog is an Animal, and it needs the Animal part set up before the Dog part.

Dog biscuit = new Dog("Biscuit", "Golden Retriever");
biscuit.Eat();    // From Animal โ€” inherited!
biscuit.Fetch();  // From Dog โ€” its own trick
Console.WriteLine(biscuit.Name);  // "Biscuit" โ€” inherited property
Console.WriteLine(biscuit.Breed); // "Golden Retriever" โ€” Dog's own

Dog gets everything Animal has, plus its own stuff. Free code. That's the deal.

2. The "is-a" Rule

Inheritance models an "is-a" relationship:

  • A Dog is-a Animal โœ…
  • A Cat is-a Animal โœ…
  • A Car is-a Animal โŒ (don't do this)

If the sentence "X is a Y" sounds ridiculous, don't use inheritance. This is the single most important rule. Violate it and you'll end up with a Car that can Eat(), and nobody wants that.

3. Method Overriding - Same Name, Different Behavior

Here's where it gets interesting. What if every animal "speaks" differently? You want Animal to define a Speak() method, but let each subclass customize it. Enter virtual and override:

class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public virtual string Speak()
    {
        return "...";
    }
}

class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public override string Speak()
    {
        return "Woof!";
    }
}

class Cat : Animal
{
    public Cat(string name) : base(name) { }

    public override string Speak()
    {
        return "Meow!";
    }
}
  • virtual on the base method means "subclasses are allowed to replace this."
  • override on the subclass method means "I'm replacing the base version."

Without virtual, you can't override. C# makes you be explicit about this โ€” no accidental overrides.

4. Polymorphism - The Magic Part

Here's the real payoff. Because Dog and Cat are both Animal, you can treat them as Animal โ€” and the right Speak() gets called automatically:

Animal[] zoo = [
    new Dog("Biscuit"),
    new Cat("Whiskers"),
    new Dog("Rex"),
    new Cat("Luna")
];

foreach (Animal animal in zoo)
{
    Console.WriteLine($"{animal.Name} says: {animal.Speak()}");
}

Output:

Biscuit says: Woof!
Whiskers says: Meow!
Rex says: Woof!
Luna says: Meow!

The variable type is Animal, but the actual object decides which Speak() runs. This is polymorphism โ€” Greek for "many forms." One method name, many behaviors, determined at runtime.

This is incredibly useful. You can write a method that accepts Animal and it'll work with any current or future subclass โ€” Parrot, Snake, Hamster โ€” without changing a single line.

5. protected - The Family Secret

Remember access modifiers from Day 10? protected is the one we skipped. It means "accessible to this class and any class that inherits from it":

class Animal
{
    public string Name { get; set; }
    protected int energy = 100;

    public Animal(string name)
    {
        Name = name;
    }

    public void Eat()
    {
        energy += 20;
        Console.WriteLine($"{Name} eats. Energy: {energy}");
    }
}

class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public void Fetch()
    {
        energy -= 30;  // Can access because Dog inherits from Animal
        Console.WriteLine($"{Name} fetches! Energy: {energy}");
    }
}

Outside code can't touch energy directly, but Dog can โ€” because it's family.

6. sealed and abstract - The Extremes

Two important keywords that sit at opposite ends of the inheritance spectrum:

abstract โ€” "This class is incomplete. You MUST inherit from it and fill in the blanks. You cannot create an instance of it directly."

abstract class Shape
{
    public abstract double Area();  // No body โ€” subclasses MUST implement this
}

class Circle : Shape
{
    public double Radius { get; }

    public Circle(double radius) { Radius = radius; }

    public override double Area() => Math.PI * Radius * Radius;
}

class Rectangle : Shape
{
    public double Width { get; }
    public double Height { get; }

    public Rectangle(double w, double h) { Width = w; Height = h; }

    public override double Area() => Width * Height;
}

// Shape s = new Shape();  // ERROR! Can't instantiate abstract class
Shape s = new Circle(5);   // Fine โ€” Circle is concrete
Console.WriteLine(s.Area()); // 78.54...

Use abstract when the base class is just a concept โ€” "Shape" isn't a real shape, it's an idea. You can't draw "a shape." You draw a circle or a rectangle.

sealed โ€” "Nobody can inherit from this class. It's done. Final. No children."

sealed class FinalDog : Animal
{
    public FinalDog(string name) : base(name) { }
}

// class SuperDog : FinalDog { }  // ERROR! Can't inherit from sealed class

Use sealed when you want to prevent inheritance for security or design reasons. The string class in .NET is sealed, for example.

7. base Keyword - Calling Your Parent

We saw base(name) in constructors. You can also use base to call the parent's version of an overridden method:

class Animal
{
    public virtual string Describe()
    {
        return "I'm an animal";
    }
}

class Dog : Animal
{
    public override string Describe()
    {
        return base.Describe() + " and also a good boy";
    }
}

Console.WriteLine(new Dog().Describe());
// I'm an animal and also a good boy

Useful when you want to extend behavior rather than completely replace it.

8. Your Homework: Shape Calculator

Create an abstract class Shape with an abstract method double Area() and a regular method string Describe() that returns $"{GetType().Name}: Area = {Area():F2}".

Then create three subclasses: Circle, Rectangle, and Triangle.

abstract class Shape
{
    public abstract double Area();
    public string Describe() => $"{GetType().Name}: Area = {Area():F2}";
}

class Circle : Shape
{
    public double Radius { get; }
    public Circle(double radius) { Radius = radius; }
    public override double Area() => Math.PI * Radius * Radius;
}

class Rectangle : Shape
{
    public double Width { get; }
    public double Height { get; }
    public Rectangle(double w, double h) { Width = w; Height = h; }
    public override double Area() => Width * Height;
}

class Triangle : Shape
{
    public double Base { get; }
    public double Height { get; }
    public Triangle(double b, double h) { Base = b; Height = h; }
    public override double Area() => 0.5 * Base * Height;
}

Shape[] shapes = [new Circle(5), new Rectangle(4, 6), new Triangle(3, 8)];
foreach (Shape shape in shapes)
{
    Console.WriteLine(shape.Describe());
}

Output:

Circle: Area = 78.54
Rectangle: Area = 24.00
Triangle: Area = 12.00

Bonus challenge: add a Perimeter() abstract method and implement it for all three shapes.

For the complete reference, see the inheritance tutorial on Microsoft Learn and the polymorphism guide.

Summary of Day 11

  • Inheritance (class Dog : Animal) lets you build on existing classes โ€” reuse code, extend behavior.
  • The "is-a" rule: only inherit when the relationship genuinely makes sense.
  • virtual + override let subclasses replace base class behavior.
  • Polymorphism: one variable type, many actual behaviors at runtime. The object decides what happens.
  • protected: visible to the class and its children, invisible to everyone else.
  • abstract: "this class is a blueprint only โ€” you must fill in the blanks."
  • sealed: "this class is final โ€” no inheritance allowed."
  • base: call the parent class's constructor or method from a subclass.

Tomorrow: we'll talk about Interfaces โ€” like abstract classes, but even more flexible. If inheritance is "is-a", interfaces are "can-do." Your objects are about to sign some contracts. ๐Ÿ“

See you on Day 12!

Share
FM

Farhad Mammadov

.NET Engineer & Cloud Architect ยท Bayern, Germany. Writing about scalable backend systems, AWS, and SRE.