Back to: SOLID Design Principles in C#
Liskov Substitution Principle in C# with Examples
In this article, I am going to discuss the Liskov Substitution Principle in C# with Examples. Please read our previous article before proceeding to this article where we discussed the Open-Closed Principle in C# with Examples. The Letter L in SOLID stands for Liskov Substitution Principle which is also known as LSP. As part of this article, we are going to discuss the following pointers in detail.
- What is Liskov Substitution Principle in C#?
- Example Without using the Liskov Substitution Principle in C#
- Example Using the Liskov Substitution Principle in C#
What is the Liskov Substitution Principle in C#?
The Liskov Substitution Principle is a Substitutability principle in object-oriented programming Language. This principle states that, if S is a subtype of T, then objects of type T should be replaced with objects of type S.
So, the Liskov Substitution Principle says that the object of a derived class should be able to replace an object of the base class without bringing any errors in the system or modifying the behavior of the base class. That means child class objects should be able to replace parent class objects without compromising application integrity.
In simple words, we can say that when we have Parent-Child relationships i.e. Inheritance Relationships between two classes, then, if we successfully replace the object/instance of a parent class with an object/instance of the child class, without affecting the behavior of the base class instance, then it is said to be in Liskov Substitution Principle. If you are not getting this point properly, don’t worry, we will see some real-time examples to understand this concept.
For example, a father is a teacher whereas his son is a doctor. So here, in this case, the son can’t simply replace his father even though both belong to the same family.
Example Without using the Liskov Substitution Principle in C#:
Let us first understand one example without using the Liskov Substitution Principle in C#, then we will see the problem if we are not following the Liskov Substitution Principle and then we will see how we can overcome such problems using Liskov Substitution Principle. In the following example, first, we create the Apple class with the method GetColor. Then we create the Orange class which inherits the Apple class as well as overrides the GetColor method of the Apple class. The point is that an Orange cannot be replaced by an Apple, which results in printing the color of the apple as Orange as shown in the below example.
using System; namespace SOLID_PRINCIPLES.LSP { class Program { static void Main(string[] args) { Apple apple = new Orange(); Console.WriteLine(apple.GetColor()); } } public class Apple { public virtual string GetColor() { return "Red"; } } public class Orange : Apple { public override string GetColor() { return "Orange"; } } }
As you can see in the above example, Apple is the base class and Orange is the child class i.e. there is a Parent-Child relationship. So, we can store the child class object in the Parent class Reference variable i.e. Apple apple = new Orange(); and when we call the GetColor i.e. apple.GetColor(), then we are getting the color Orange, not the color of an Apple. That means once the child object is replaced i.e. Apple storing the Orange object, the behavior is also changed. This is against the LSP Principle. The Liskov Substitution Principle states that even if the child object is replaced with the parent, the behavior should not be changed. So, in this case, if we are getting the color of Apple instead of Orange, then it follows the Liskov Substitution Principle. That means there is some issue with our software design. Let us see how to overcome the design issue and makes the application follow Liskov Substitution Principle using C# Langauge.
Example Using the Liskov Substitution Principle in C#
Let’s modify the previous example to follow the Liskov Substitution Principle using C# Language. Here, first, we need a generic base Interface i.e. IFruit which is going to be the base class for both Apple and Orange classes. Now, you can replace the IFruit variable can be replaced with its subtypes either Apple or Orage and it will behave correctly. Now, you can see in the below code, we created the super IFruit as an interface with the GetColor method and then the Apple and Orange classes inherited from the Fruit class and implement the GetColor method.
using System; namespace SOLID_PRINCIPLES.LSP { class Program { static void Main(string[] args) { IFruit fruit = new Orange(); Console.WriteLine($"Color of Orange: {fruit.GetColor()}"); fruit = new Apple(); Console.WriteLine($"Color of Apple: {fruit.GetColor()}"); Console.ReadKey(); } } public interface IFruit { string GetColor(); } public class Apple : IFruit { public string GetColor() { return "Red"; } } public class Orange : IFruit { public string GetColor() { return "Orange"; } } }
Now, run the application and it should give the output as expected as shown in the below image. Here we are following the LSP as we are now able to change the object with its subtype without affecting the behavior.
So, now Fruit can be any type and any color, but orange cannot be the color red and an apple cannot be of the color orange, meaning we cannot replace orange with an apple but fruit can be replaced with an orange or an apple because they are both Fruits, an apple is not an orange and an orange is not an apple.
Note: The point that you need to remember is that, as we have the Inheritance concept, that does not mean we can create the relationship between classes randomly. We will not get any error or exception, but the behavior that we expect might not get. So, always make sure that the relationship and functionality that we are providing make sense. If make sense, then go for the inheritance relationship, if not, then don’t go for the inheritance relationship.
In the next article, I am going to discuss the Interface Segregation Principle in C# with Examples. Here, in this article, I try to explain the Liskov Substitution Principle in C# with Examples. I hope you enjoy this Liskov Substitution Principle article.
Thank you. Very good example and well explained.
good explanation with real time example.
I can do the same implementation without creating an interface.
Apple apple = new Orange();
Console.WriteLine(apple.GetColor());
apple = new Apple();
Console.WriteLine(apple.GetColor());
It will gives the output as expected.
Could please explain with any other example.
Apple apple = new Orange();
this will give output as orange. As per principle it should not print “The point is that an Orange cannot be replaced by an Apple, which results in printing the color of apple as Orange”.
So if you call by Abstract object it will not allow you to create object like this
Apple apple = new Orange();
for the example without using the Liskov Substitution Principle, you should remove the “virtual” keyword and replace the “override” by “new” instead. So, it will more understandable.
I think your example that without using the Liskov Substitution Principle is wrong, it follows the principal.
Apple apple = new Orange();
Console.WriteLine(apple.GetColor());
Orange apple1 = new Orange();
Console.WriteLine(apple1.GetColor());
I agree with PHINEAS VUONG, with new key word will not follow the principal
i think a way it might be more clear is to say Fruit can be any type and any color, but a orange cannot be the color red and an apple cannot be of color orange , meaning we cannot replace a orange with an apple but fruit can be replaced with an orange or an apple because they are both Fruits, a apple is not an orange and a orange is not a apple.
Lets say the base class was Cooldrinks, and a sub class was Coke and the other Fanta Orange, then we would be able to replace the Base Type (cooldrinks) with either Coke or Fanta Orange because they are both Cooldrinks but if we ask for a Coke we do not expect or want a Fanta we want a Coke , if we ask for cooldrink we do not know what we will receive and any of the 2 would be sufficient. So if the base type is the same any of the 2 would work but we specifically want one sub type another would not be sufficient.
Hope this helps out.
Great explanation. The comment added by DIEDERIK clearing the all the confusion.
Very useful and understandable content. Thanks for your time.
Very Confusing! I fully agree with DIEDERIK.
We can also achieve using the interface for Liskov Substitution Principle in C#
using System;
namespace With_Liskov_Substitution_Principle
{
interface Fruit
{
string GetColor();
}
public class Apple : Fruit
{
public string GetColor()
{
return “Red”;
}
}
public class Orange : Fruit
{
public string GetColor()
{
return “Orange”;
}
}
class Program
{
static void Main(string[] args)
{
Orange a = new Orange();
Console.WriteLine(a.GetColor());
Console.ReadKey();
}
}
}
I am immediately impressed by the author’s expertise and by his lucid explanation of the work.
I think this example is not correct , because when I substitute base class instance with child class instance I get the correct response
Apple fruit1 = new Orange();
Console.WriteLine(fruit1.GetColor()); //orange
Apple fruit2 = new Apple();
Console.WriteLine(fruit2.GetColor()); // red