Dataslope logoDataslope

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.

Code Block
C# 13

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.

Code Block
C# 13

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.

Code Block
C# 13

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.

Code Block
C# 13

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

Challenge
C# 13
Shapes with polymorphism

Complete the Shape hierarchy:

  • Shape is abstract with an abstract double Area().
  • Circle takes a radius and returns π × r².
  • Rectangle takes width and height and returns w × h.

Program.cs builds an array of shapes and prints exactly:

12.57
12.00

Test your understanding

QuestionSelect one

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

QuestionSelect one

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

On this page