Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
What is Liskov Substitution principle and how to implement in C#?
The Liskov Substitution Principle (LSP) is one of the five SOLID principles in object-oriented programming. It states that derived types must be completely substitutable for their base types without altering the correctness of the program. In other words, objects of a superclass should be replaceable with objects of its subclasses without breaking the application.
Definition
According to LSP, we should be able to treat a child class as though it were the parent class. This means that all derived classes should retain the functionality of their parent class and cannot replace any functionality the parent provides in a way that violates the expected behavior.
Violating Liskov Substitution Principle
Here's an example that violates LSP. The Circle class inherits from Ellipse but changes the expected behavior −
using System;
public class Ellipse {
public double MajorAxis { get; set; }
public double MinorAxis { get; set; }
public virtual void SetMajorAxis(double majorAxis) {
this.MajorAxis = majorAxis;
}
public virtual void SetMinorAxis(double minorAxis) {
this.MinorAxis = minorAxis;
}
public virtual double Area() {
return MajorAxis * MinorAxis * Math.PI;
}
}
public class Circle : Ellipse {
public override void SetMajorAxis(double majorAxis) {
base.SetMajorAxis(majorAxis);
this.MinorAxis = majorAxis; // In a circle, each axis is identical
}
}
public class Program {
public static void Main() {
Ellipse shape = new Circle();
shape.SetMajorAxis(5);
shape.SetMinorAxis(4);
Console.WriteLine("Expected area (5*4*?): " + (5 * 4 * Math.PI));
Console.WriteLine("Actual area: " + shape.Area());
Console.WriteLine("LSP violated: Circle doesn't behave like Ellipse");
}
}
The output of the above code is −
Expected area (5*4*?): 62.83185307179586 Actual area: 62.83185307179586 LSP violated: Circle doesn't behave like Ellipse
Following Liskov Substitution Principle
To fix the LSP violation, we create separate classes with their own specific behaviors instead of forcing inheritance where it doesn't fit naturally −
using System;
// Abstract base class for shapes
public abstract class Shape {
public abstract double Area();
public abstract void Display();
}
public class Ellipse : Shape {
public double MajorAxis { get; set; }
public double MinorAxis { get; set; }
public Ellipse(double majorAxis, double minorAxis) {
MajorAxis = majorAxis;
MinorAxis = minorAxis;
}
public override double Area() {
return MajorAxis * MinorAxis * Math.PI;
}
public override void Display() {
Console.WriteLine($"Ellipse - Major: {MajorAxis}, Minor: {MinorAxis}, Area: {Area():F2}");
}
}
public class Circle : Shape {
public double Radius { get; set; }
public Circle(double radius) {
Radius = radius;
}
public override double Area() {
return Radius * Radius * Math.PI;
}
public override void Display() {
Console.WriteLine($"Circle - Radius: {Radius}, Area: {Area():F2}");
}
}
public class Program {
public static void ProcessShape(Shape shape) {
shape.Display(); // Works correctly for both Circle and Ellipse
}
public static void Main() {
Shape[] shapes = {
new Circle(5),
new Ellipse(6, 4)
};
foreach (Shape shape in shapes) {
ProcessShape(shape); // LSP satisfied - any Shape works
}
}
}
The output of the above code is −
Circle - Radius: 5, Area: 78.54 Ellipse - Major: 6, Minor: 4, Area: 75.40
Key Rules for LSP Compliance
| Rule | Description |
|---|---|
| Preconditions | Cannot be strengthened in derived classes |
| Postconditions | Cannot be weakened in derived classes |
| Invariants | Must be preserved in derived classes |
| Exceptions | New exceptions cannot be thrown by derived methods |
Benefits of Following LSP
-
Predictable behavior − Client code works consistently with base and derived classes.
-
Better maintainability − Changes to derived classes don't break existing code.
-
Proper inheritance − Ensures inheritance represents true "is-a" relationships.
-
Polymorphism support − Enables safe use of polymorphism without unexpected side effects.
Conclusion
The Liskov Substitution Principle ensures that derived classes can replace their base classes without altering program correctness. When LSP is violated, it often indicates that inheritance is being misused and composition or separate class hierarchies should be considered instead.
