Search code examples
c#.netsolid-principlesliskov-substitution-principle

C# compile error trying to apply Liskov Substitution Principle


I am reading a book about Design Patterns, and there's this chapter on the Liskov Substitution Principle that says to do this (and other tasks) to achieve the principle:

Parameter types in a method of a subclass should match or be more abstract than parameter types in the method of the superclass. Sounds confusing? Let’s have an example.

Say there’s a class with a method that’s supposed to feed cats: feed(Cat c).
Client code always passes cat objects into this method.

◦ Good: Say you created a subclass that overrode the method so that it can feed any animal (a superclass of cats): feed(Animal c). Now if you pass an object of this subclass instead of an object of the superclass to the client code, everything would still work fine. The method can feed all animals, so it can still feed any cat passed by the client.

◦ Bad: You created another subclass and restricted the feed- ing method to only accept Bengal cats (a subclass of cats): feed(BengalCat c). What will happen to the client code if you link it with an object like this instead of with the orig- inal class? Since the method can only feed a specific breed of cats, it won’t serve generic cats passed by the client, breaking all related functionality.

To me everything seemed fine, till I decided to try implement this example in C#.
This is the code I wrote to understand better the "Good" part of the example:

public class Animal { }
public class Cat : Animal { }


public class AnimalFeeder
{
    public virtual void Feed(Cat c)
    {
        Console.WriteLine("Feeding a cat...");
    }
}

public class GenericFeeder : AnimalFeeder
{
    public override void Feed(Animal a) // Compile Error - No suitable method found to override 
    {
        Console.WriteLine("Feeding an animal...");
    }
}

The only problem is that I get the error above,
maybe I misunderstood the example and didn't wrote the correct code,
if yes can someone help me correct the code in the proper way?

Thank you in advance!


Solution

  • Assuming that the book you mean is “Dive into Design Patterns“ by Alexander Shvets, of which I found a readable copy online, there is actually the following quote following your excerpts:

    In most modern programming languages, especially statically typed ones (Java, C#, and others), these rules are built into the language. You won’t be able to compile a program that violates these rules.

    I also found the exact description of these animal feeder classes in a post by Tamerlan Gudabayev on dev.to (which makes me question the copyright rules on dev.to). The programming language that is used for the examples there is PHP, which is dynamically typed and as such does not suffer from the issues that you have when attempting to implement this with C#.


    So unfortunately, that description of LSP is not the best. To me, the description confuses two things into one, focusing too much on subclassing the AnimalFeeder.

    LSP instead focuses on the simplistic idea that you can pass a more specific object into something that expects the base type. So applying this to the AnimalFeeder example: Let’s assume that your Feed method feeds an animal, i.e. AnimalFeeder.Feed(Animal). Then, going by LSP, you can also pass a specific animal, e.g. a cat, to the method and it should work fine since Cat would be a subclass of Animal.

    This of course if unspecific about the type hierarchy of AnimalFeeder and whether there are subclasses or not. But you can apply LSP there too if you look at it from the reverse. Let’s assume that there is a method that takes an AnimalFeeder:

    public void Test(AnimalFeeder feeder)
    {
        Animal animal = GetAnimalFromSomewhere();
        feeder.Feed(animal);
    }
    

    So here you can apply LSP again to look at the constraints for subclasses of AnimalFeeder. If a subclass of AnimalFeeder would now have a Feed method that is not able to deal with any animal, then LSP would be violated here.