Inheritance and Polymorphism
Reusing and extending behavior across a family of related classes
Real-world things come in families. A Dog is a kind of Animal. A SavingsAccount is a kind of BankAccount. A Circle and a Rectangle are both kinds of Shape.
Object-oriented languages let you mirror that "is-a" structure in code through inheritance. And once you have a family of related types, you can write code that doesn't care which member it's holding — that's polymorphism.
Inheritance: the basic mechanics
A base class holds the common state and behavior. A derived class adds to or overrides what the base provides.
Dog doesn't have to re-implement Name or Eat. They come "for
free" because every Dog is an Animal.
A class hierarchy as a picture
The arrow always points "up" toward the more general type. The shared parts live at the top.
Overriding behavior with virtual and override
Sometimes a derived class needs to change how a base method
works, not just add new methods. The base marks the method
virtual; the derived class marks its replacement override.
Look at the last block carefully. The variable a is declared as
Animal. The actual object is sometimes a Dog, sometimes a
Cat. At runtime, a.Speak() calls the right version based
on what a really is.
That ability — "one call site, many possible behaviors" — is polymorphism.
Why polymorphism matters
Imagine writing this without polymorphism:
if (kind == "dog") BarkLikeADog(name);
else if (kind == "cat") MeowLikeACat(name);
else if (kind == "cow") MooLikeACow(name);Every new animal forces you to edit every if/else chain in the
program. With polymorphism, you just add a new derived class with
its own Speak(), and every existing call site that says
animal.Speak() already does the right thing.
abstract classes: forcing the family to fill in the blanks
Sometimes the base class can't define a method itself — it only knows that every subclass must provide one. That's an abstract method on an abstract class.
Shape cannot be instantiated directly — there's no such thing
as "a shape" with no specific kind. But the array Shape[] can
hold any subclass, and Describe() uses each subclass's Area()
without caring which kind it is.
Interfaces: a contract, not a family
An interface is a list of methods/properties that a class promises to provide. It's pure shape — no fields, no behavior.
A class can only inherit from one base class but can implement many interfaces. Interfaces are the everyday tool for saying "these unrelated types all happen to support this same capability."
Composition vs inheritance
A common beginner trap is to use inheritance for every relationship. A better rule:
- Use inheritance when the relationship is genuinely "is-a" and the subclass extends the base in a substitutable way.
- Use composition ("has-a") when one class merely uses another.
A Car has an Engine — it isn't a kind of Engine. So
Car should hold a field of type Engine, not inherit from it.
Practice
Complete the Shape hierarchy:
Shapeis abstract with an abstractdouble Area().Circletakes a radius and returns π × r².Rectangletakes width and height and returns w × h.
Program.cs builds an array of shapes and prints exactly:
12.57
12.00
Test your understanding
Why is polymorphism useful?
It eliminates the need for classes
One call site (shape.Area()) automatically does the right thing for every subclass, so new subclasses don't require changes to existing code
It makes programs run faster
It allows multiple inheritance
When should you use inheritance vs composition?
Always prefer inheritance
Always prefer composition
Inheritance for unrelated types, composition for related types
Use inheritance for genuine "is-a" relationships; use composition for "has-a" relationships