Liskov substitution principle (LSP)

This is the “L” in SOLID.

LSP suggests that IS-A relationships between classes should be replaced with IS-SUBSTITUTABLE-FOR.

In C#, the LSP is often implemented through interfaces and polymorphism.

Definition

“The Liskov Substitution Principle states that Subtypes must be substitutable for their base types.”

  • Named for Barbara Liskov, who first described the principle in 1988.

In order for substitutability to work child classes must not:

  • Remove base class behavior
  • Violate base class invariants, these could be any constraints defined (or reasonable assumed by clients) on the base classes.

Why follow LSP?

If the classes are none substitutable then polymorphism will not work and there will be code smells added such as if conditions / switches which will be hard to maintain. This also violates the Open / Closed Principle (OCP)

You will also have issues such as NotImplementedException(); being left in inherited methods. This violates the Interface Segregation Principle (ISP)

References & Sample Code

Full disclosure I asked Chat GPT for this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface IShape {
int GetArea();
}

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

public int GetArea() {
return Width * Height;
}
}

class Square : IShape {
public int SideLength { get; set; }

public int GetArea() {
return SideLength * SideLength;
}
}

In this example, Rectangle and Square are both classes that implement the IShape interface. The GetArea method is defined in the interface and implemented by both classes. Now, in any part of our code, if we have a variable of type IShape, we can assign it a Rectangle or Square object without any issue.

1
2
3
4
5
6
void Main() {
IShape shape1 = new Rectangle { Width = 2, Height = 3 };
IShape shape2 = new Square { SideLength = 3 };
Console.WriteLine(shape1.GetArea()); // 6
Console.WriteLine(shape2.GetArea()); // 9
}

Here, shape1 is assigned a Rectangle object and shape2 is assigned a Square object, both of them implements the IShape interface and have a GetArea method, so it can be called without any issue and the program will work as expected.

It’s also important to notice that in this example we can not add any method in the Square class that does not exist in the IShape interface, otherwise it will cause a compilation error, that’s what makes it a substitute.