Abstract class vs interface

7/8/2022
7 minute read

You are coming back from grocery shopping and have a walk through the park, you see some animals like a dog, a cat, and two ducks. All of them are drinking at the nearby pond. While you walking out of the park, you wondered: "The cat looked rather old, why is it not dead?". Anyway, you moved on and went to the ATM and get some money. You press the "Withdraw Money" button, enter your pin and finally get the money.

Now what does that have to do with interfaces or abstract classes? Well in our example the animal is the abstract class. We know that animals have some common behaviors. For example, they are alive at a certain age. They drink water and eat food. So all animal share some common behavior and properties. You asked yourself why is the cat not dead. Well all of them have an age but that doesn't mean that all animals die at the same time. All of them sleep, but a dog or a cat sleeps a different amount of time. So what an abstract class is, is just a common set of behavior they share. But sure, the nitty gritty details can vary between them. Another way of seeing that is: "Hey that is an animal and there is no way of being more specific."

And the interface? Look at the ATM. You wanted to withdraw money. So you and the ATM have kind of an operational contract. You want money, and the ATM wants your PIN. You don't care how the ATM withdraws your money. Whether or not a person sits inside the ATM and checks online if you have enough funds or if there is a big SQL database is in the background... you don't care. You care that the operation is executed. And that is all: An interface is just a contract that defines which operations an object performs.

abstract class

Defining an abstract class can be done like this:

public abstract class Animal
{
    // protected set means every derived class can set the value
    public int Age { get; protected set; }
    // private set means only the abstract Animal class is allowed to set the value
    public bool IsDead { get; private set; }
    
    // As every animal drinks water differently, we can't define a common way of drinking
    public abstract void DrinkWater();

    // We can also define a function as virtual
    // virtual means every derived class CAN override that method but does not have to
    public virtual void Move() { ... } 

    // The function is not virtual so no one can override it
    // Also it is protected so only Animal or every derivate can call that function
    // but not the outside world
    protected void Die() { IsDead = true; }
}

public class Dog : Animal
{
    // We HAVE TO override that method because in "Animal" it is abstract meaning
    // it has 0 logic
    public override void DrinkWater() {...}

    // You see, no Move method here. We don't have to override
    // as there is already a template implementation in place 
}

public class Duck : Animal
{
    // We HAVE TO override that method because in "Animal" it is abstract meaning
    // it has 0 logic
    public override void DrinkWater() {...}

    // This time we want to override the Move method
    // And if it moves, it dies :D
    public override void Move() => Die();
}

So we can see a lot of variety in what we can do with abstract classes. You can see it from that angle: If we have kind of a template, which needs here and there concrete details, then we are speaking most likely of an abstract class. One class can only implement one abstract class but "unlimited" interfacesWell there are technical limitations. Your compiler will run out of stack and heap memory as it has to build that extremely deep or wide dependency tree..

Now you could say: "Hey Steven, do I need the abstract part here? I can just use a normal "class", can't I?. Well, something along the line of this:

public class Animal
{
    public int Age { get; protected set; }
    public bool IsDead { get; private set; }
    
    // As abstract is not allowed inside a non abstract class 
    // we have to find another way
    public virtual void DrinkWater()
    {
        // Or just return???
        throw new NotImplementedException();
    }
}

That is bad for a multitude of reasons.

  1. Now everyone can instantiate an animal like this: var animal = new Animal();. That did not work with the abstract class. Now we say: Hey, animal is something very concrete!
  2. No one is forced to implement DrinkWater anymore. Well that's bad.
  3. That brings us to the NotImplementedException or even if we just return nothing. We shifted from a compile time error to a runtime error. That is in 99.9% of cases bad!
  4. As mentioned in my SOLID article, we violate here the Liskov Substitution Principle.

So just don't.

interfaces

Interfaces are just a contract. Let's say we have a repository to save some text-data. As a consumer do we care where it is saved. If we call save and afterwards load, we are just interested that the stuff is there again. So interfaces are just a contract on which operations someone or something can do, with an expected and well-defined input and output. That is it.

interface IRepository
{
    // Everything has to be public, therefore we need no visibility modifier
    int SaveText(string text);
    string LoadText(int identifier);
}

Now in the background could be a SQL-Database, or a Document Database or a simple text file. It doesn't matter from the outside world.

When should I use what?

As a rule of thumb, parameters of functions should use interfaces. SOLID Principles told you that. But on a serious note: There are multiple reasons:

  • abstract classes are normally implementation details. The external world should not rely on implementation details.
  • As a lot of classes can implement an interface, you can freely switch out the class without changing your method. Hello Open-Close principle.
  • abstract classes tend to couple your code together a lot! They make it also harder to unit test in a lot of cases.
  • abstract classes are meant for sharing internal behavior or state of different objects which represent a is-a relationship.

Short: Abstract classes are used for Modelling a class hierarchy of similar looking classes (For example Animal can be abstract class and Human , Lion, Tiger can be concrete derived classes)

AND

Interface is used for Communication between 2 similar / non similar classes which does not care about type of the class implementing Interface(e.g. Height can be interface property and it can be implemented by Human , Building , Tree. It does not matter if you can eat , you can swim you can die or anything.. it matters only a thing that you need to have Height (implementation in you class) ).

Source

You can also mix that:

public interface IFly
{
    void MoveUp();
    void MoveDown();
}

public abstract class Bird : IFly
{
    public virtual void MoveUp() => FlapWings();
    public abstract void MoveDown();
}

public class Duck : Bird
{
    public override void MoveDown() => AntiFlapWings();
}

Now if we have a method which wants IFly we can use Duck:

var duck = new Duck();
IBelieveICanFly(duck);

void IBelieveICanFly(IFly fly)
{
    // ...
}

Bonus: Default interface methods

Since C# 8 you can also define a template body on interface level like that:

IHelloWorldPrinter printer = new HelloWorldPrinter();
printer.PrintHelloWorld(); // Prints Hello World

HelloWorldPrinter anotherPrinter = new HelloWorldPrinter();
anotherPrinter.PrintHelloWorld(); // Compiler error

public interface IHelloWorldPrinter
{
    void PrintHelloWorld() => Console.Write("Hello World");
}

public class HelloWorldPrinter : IHelloWorldPrinter { }

So it is kind of an abstract class and blurs the line a lot. There are two options:

  • Either you implement the body and then you can use it on class level
  • Or you can only use the body if you work on the interface type

Now my personal opinion: I am not a big fan of that feature. I don't see any big usage for it and makes the usage of abstract class and interface more complicated than it helps.

Conclusion

interfaces are operational contracts that help you achieve loose coupling. abstract classes on the other side help you to create a common set of shared behaviour for a set of subclasses. Abstract classes try to reduce code duplication in objects which represent a is-a relation.

Interfaces can have private methods

Let's drop some "useless" knowledge here. Interfaces can have private methods. This comes with the C# 8 feature: "Default interface methods".

Why are sealed classes faster in C#? And should I seal them?

In C# we can add the sealed modifier to a class to indicate that no one is allowed to derive / inherit from that class. Let's have a look at the compiler in certain scenarios what happens if you seal a class.

static abstract interfaces and generic math

Besides the big announcements of .NET 6 there are also some smaller features. I want to show case a special one: static abstract interfaces. With this you have ability to extend the contract in that sense, that an implementing class has to provide also static methods. This feature is right now flagged as preview, but you can use it if you want.

This also enables generic math operations on an interface level.

An error has occurred. This application may no longer respond until reloaded. Reload x