Lesson 97 min read

Inheritance & Polymorphism

Build on what already exists — family trees for code

What Is Inheritance?

Inheritance is like a family recipe book. Your grandma wrote a recipe for basic bread. Your mom inherited that recipe and added cinnamon to make cinnamon bread. You inherited mom's recipe and added frosting to make cinnamon rolls. Each generation builds on what came before without rewriting the whole recipe.

In C#, a child class (also called a "derived class") inherits all the properties and methods from its parent class (the "base class"). The child can then add new stuff or change existing behavior.

C# uses a colon : for inheritance: class Child : Parent

Basic Inheritance

class Animal
{
public string Name { get; set; }
public int Legs { get; set; }
public Animal(string name, int legs)
{
Name = name;
Legs = legs;
}
public virtual string Speak() // virtual = children CAN override this
{
return "...";
}
public string Describe()
{
return $"{Name} has {Legs} legs";
}
}
class Dog : Animal // Dog inherits from Animal
{
public string Breed { get; set; }
// Call the parent constructor with 'base'
public Dog(string name, string breed) : base(name, 4)
{
Breed = breed;
}
public override string Speak() // override the parent's method
{
return "Woof!";
}
}
class Cat : Animal
{
public Cat(string name) : base(name, 4) { }
public override string Speak() => "Meow!";
}
var dog = new Dog("Rex", "Husky");
var cat = new Cat("Whiskers");
Console.WriteLine($"{dog.Describe()} — Breed: {dog.Breed}");
Console.WriteLine($"{dog.Name} says: {dog.Speak()}");
Console.WriteLine($"{cat.Describe()}");
Console.WriteLine($"{cat.Name} says: {cat.Speak()}");
Output
Rex has 4 legs — Breed: Husky
Rex says: Woof!
Whiskers has 4 legs
Whiskers says: Meow!

Polymorphism — One Interface, Many Forms

Polymorphism is a fancy word for a simple idea (used everywhere in data structures like binary search trees): you can treat a child object as if it were the parent type, and the right method still gets called. It's like having a remote control that works on any TV brand — you press "volume up" and each TV handles it its own way.

This is incredibly powerful. You can write code that works with the parent type, and it automatically works with any child type — even ones that don't exist yet!

Polymorphism & Abstract Classes

// Abstract class — can't be instantiated, only inherited
abstract class Shape
{
public string Color { get; set; }
public Shape(string color) => Color = color;
// Abstract method — children MUST implement this
public abstract double Area();
// Regular method — children inherit this as-is
public string Label() => $"{Color} {GetType().Name}";
}
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius, string color) : base(color)
=> Radius = radius;
public override double Area() => Math.PI * Radius * Radius;
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double w, double h, string color) : base(color)
{
Width = w;
Height = h;
}
public override double Area() => Width * Height;
}
// sealed = no one can inherit from this class
sealed class Square : Rectangle
{
public Square(double side, string color) : base(side, side, color) { }
}
// Polymorphism in action!
Shape[] shapes = {
new Circle(5, "Red"),
new Rectangle(4, 6, "Blue"),
new Square(3, "Green")
};
foreach (Shape shape in shapes)
{
// Each shape calculates area its own way!
Console.WriteLine($"{shape.Label()}: Area = {shape.Area():F2}");
}
Output
Red Circle: Area = 78.54
Blue Rectangle: Area = 24.00
Green Square: Area = 9.00

Key Keywords Summary

  • virtual — "Children may override this method if they want to."
  • override — "I'm replacing my parent's implementation with my own."
  • abstract — "I'm not providing an implementation. Every child must provide one." Used on classes and methods.
  • sealed — "No one can inherit from this class." It's the end of the family line.
  • base — "Call my parent's constructor or method." Like calling grandma for the original recipe.
Note: 🧬 Abstract vs Virtual: Use abstract when every child MUST have its own version (like Area() — every shape calculates it differently). Use virtual when you have a reasonable default that children CAN override but don't have to.

Quick check

Can you create an instance of an abstract class?
Classes & ObjectsInterfaces & Abstract Classes