Liskov Substitution Principle (LSP)

Liskov Substitution Principle in C# – Explained with Examples

Liskov Substitution Principle (LSP) in C#

The Liskov Substitution Principle (LSP) is one of the five SOLID principles of object-oriented design. It ensures that subtypes can stand in for their base types without breaking program behavior.

"Derived classes must be substitutable for their base classes without altering the correctness of the application."

✅ Key Concepts

  • Subclasses must not break the expected behavior of the base class.
  • Method contracts (preconditions/postconditions) must be preserved or strengthened.
  • No side effects should occur when using a subclass in place of a base class.
  • LSP enables safe polymorphism and predictable behavior.

🚫 LSP Violation Example

class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public int GetArea() => Width * Height;
}

class Square : Rectangle
{
    public override int Width
    {
        set { base.Width = base.Height = value; }
    }

    public override int Height
    {
        set { base.Width = base.Height = value; }
    }
}

Usage:

Rectangle rect = new Square();
rect.Width = 5;
rect.Height = 10;

Console.WriteLine(rect.GetArea()); // Outputs 100 instead of 50!
⚠️ Problem: The square violates the contract of the rectangle, where width and height are expected to be independent. This breaks LSP.

✅ Fix: Use Composition Instead

abstract class Shape
{
    public abstract int GetArea();
}

class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }

    public override int GetArea() => Width * Height;
}

class Square : Shape
{
    public int Side { get; set; }

    public override int GetArea() => Side * Side;
}
✅ Fix Explanation: Each shape now has its own logic. Substitution is safe and behavior is predictable.

💬 Interview Q&A

Q1: What is the Liskov Substitution Principle?

A: LSP states that you should be able to use a derived class in place of a base class without affecting program correctness.

Q2: What are the core rules?

  • Subclasses must honor the contracts of base classes.
  • Do not add unexpected validation or side effects.
  • Ensure the method output is consistent with the base version.

Q3: How do you detect LSP violations?

  • When a subclass changes expected output or throws exceptions not thrown by the base.
  • Behavior breaks when switching from base class to subclass.

Q4: How can LSP be enforced?

  • Favor composition over inheritance.
  • Split behavior via interfaces.
  • Don’t override base methods with conflicting behavior.

🕊 Real-Life Analogy

class Bird { public virtual void Fly() { } }

class Ostrich : Bird 
{ 
    public override void Fly() => throw new NotSupportedException(); 
}

Issue: Ostrich violates LSP because it can’t fly, yet the base class assumes all birds can.

Fix: Use interfaces like:

interface IFlyable { void Fly(); }
interface IWalkable { void Walk(); }

📌 Summary

  • LSP ensures predictable behavior when working with inheritance.
  • It supports polymorphism by requiring consistent contracts.
  • Helps in writing maintainable, bug-free object-oriented code.
✅ When in doubt, ask: “Can I replace this base class with a subclass without breaking anything?”

Comments

Popular posts from this blog

Debouncing & Throttling in RxJS: Optimizing API Calls and User Interactions

Promises in Angular

Comprehensive Guide to C# and .NET Core OOP Concepts and Language Features