Polymorphism — Designing for Substitution, Not Reuse
Why polymorphism is about trust, not sharing code
Polymorphism is not a tool for reuse.
It is a tool for trust.
This is Part 5 of the series:
- Classes in C#: From First Principles to Architectural Mastery
- Abstract Class vs Interface — A Systems View
- Composition vs Inheritance — The Physics of Change
- Encapsulation & Information Hiding — Designing for Ignorance
- Polymorphism — Designing for Substitution, Not Reuse
This article reframes polymorphism as a systems property,
not an inheritance trick.
The Most Misunderstood OOP Principle
Most developers learn polymorphism as:
“Different classes sharing a base class.”
That explanation is incomplete — and misleading.
Polymorphism is not about sharing code.
It is about preserving meaning under substitution.
What Polymorphism Really Guarantees
Polymorphism answers one question:
“Can I replace this with something else and still be safe?”
If the answer is yes, polymorphism exists.
If the answer is no, inheritance is lying to you.
Substitution Is the Point
Stream s = new MemoryStream();
This line works because:
- expectations hold
- semantics are preserved
- behavior remains valid
The system does not care which stream it is using —
only that the contract is honored.
That is polymorphism.
Reuse Is an Accident (Sometimes a Bad One)
Code reuse often motivates inheritance:
public class FileLogger : LoggerBase
But reuse alone does not justify polymorphism.
If replacing FileLogger with DatabaseLogger changes behavior in surprising ways, the abstraction is broken — even if code compiles.
Polymorphism Is a Social Contract
Every polymorphic abstraction makes a promise:
“You don’t need to know what I am —
only what I guarantee.”
Breaking that promise erodes trust across the system.
This is why polymorphism scales systems — or destroys them.
The Liskov Boundary
Liskov Substitution Principle is not academic.
It is the boundary between safety and surprise.
Ask:
- Are preconditions strengthened?
- Are postconditions weakened?
- Are invariants preserved?
If not, substitution is unsafe.
Interfaces vs Abstract Classes (Revisited)
Interfaces excel at polymorphism because:
- they define capability
- they avoid shared state
- they minimize hidden coupling
Abstract classes introduce:
- identity
- workflow
- temporal constraints
Use polymorphism where behavioral equivalence matters,
not where reuse is convenient.
Polymorphism Without Inheritance
Modern C# supports polymorphism via:
- interfaces
- delegates
- composition
- higher-order functions
Func<Order, decimal> pricingStrategy;
No hierarchy.
No base class.
Still polymorphic.
The Cost of Broken Polymorphism
When polymorphism fails:
- code becomes defensive
- conditionals replace dispatch
- abstractions leak
- fear spreads
if (stream is FileStream) { ... }
This is a smell.
The system no longer trusts its abstractions.
Polymorphism as System Stability
Polymorphism:
- localizes change
- reduces coordination
- enables parallel evolution
It is not about elegance.
It is about resilience.
The Mental Model to Keep
Polymorphism is substitutability plus trust.
Reuse is optional.
Correctness is not.
Final Takeaway
Junior developers ask:
“How do I reuse this code?”
Senior engineers ask:
“Can this be replaced without breaking meaning?”
That question defines real polymorphism.
✍️ Cristian Sifuentes
.NET / C# • Architecture • Systems Thinking
If substitution fails, polymorphism never existed.

Top comments (0)