Provides guidance on how to use inheritance.

Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.

Barbra Liskov (1087) - An elitist who overcomplicates simple concepts

Subtypes must be substitutable for their base types.

IS-A

A square IS a rectangle.

HAS-A

A square HAS four sides.
A rectangle HAS four sides.

A Square has an invariant rule: Its sides must be equal.

A Rectangle has an invariant rule: Its sides are independent.

IS-SUBSTITUTE-FOR

A rectangle IS-SUBSTITUTE-FOR a square.

NOT IS-SUBSTITUTE-FOR

A square is NOT IS-SUBSTITUTE-FOR a rectangle.

Detecting LSP Violations in Your Code

  • Type Checking

    Type checking with is or as in polymorphic code.

    foreach (var employee in employees)
    {
        if (employee == null)  <-- LSP Violation, because null checking. Employee collection should not contain nulls.
        {
            Console.WriteLine("Employee not found.");
            break;
        }
    
        if (employee is Manager)  <-- LSP Violation, because type checking
        {
            Helpers.PrintManager(employee as Manager);
            break;
        }
    
        Helpers.PrintEmployee(employee);
    }
    
    We have a type check therefore LSP is broken.

    Additional if statements will have to be added for every additional employee type.

    To make each employee type substitutable we can do one of the following:

    foreach (var employee in employees)
        Helpers.Print(employee);
    
    Override the Print method to accept an employee or manager type.

    OR

    foreach (var employee in employees)
        employee.Print();
    
    Move print functionality into the employee. This may violate SRP.
  • Not Implemented Exceptions

    Methods in classes return NotImplementedException.

    public class SmtpNotificationService : INotificationService
    {
        public void SendEmail(string to,
                              string from,
                              string body)
        {
            // Send email code
        }
    
        public void SendText(string SMSNumber,
                             string message)
        {
            throw now NotImplementedException();
        }
    }
    
    This is not substitutable for the INotificationService.

    Learn More

    Nulls Break Polymorphism

    Pluralsight course: Design Patterns Library

Polymorphism
IS-A
LSP
IS-Substitutable
LSP is a Subset of Polymorphism

Fixing LSP Violations

  • Follow the "Tell, Don't Ask" principle. Don't ask instances for their Type and then conditionally perform different actions. Instead, encapsulate that logic into the type instead then tell it to perform that action.
  • Minimise null checks with:
    • Use C# features
    • Guard clauses
    • Null Object design pattern
  • Follow ISP and be sure to fully implement interfaces.

Key Takeaways

  • Subtypes must be substitutable for their base types
  • Ensure base type invariants are enfored
  • Look for:
    • Type checking
    • Null checking
    • NotImplementedException

Other Links

stackify.com/solid-design-liskov-substitution-principle

Copyright © 2025 delaney. All rights reserved.