C# Inheritance Pt3 - Abstract Classes

Grant Riordan - Nov 20 '21 - - Dev Community

Hey peeps, so far I hope we're grasping the concepts of inheritance through a basic example of inherting class properties and methods, and then looking at a more robust interface implementation.

Now we're going to look at a Abstract Class.

What is an abstract class?

Well, they're alot like an interface except firstly you can implement and express default behaviour of class properties and methods.

However, unlike other classes, it cannot be instantiated.

Other things to remember, is that like an Interface you can declare empty methods, and have the inheriting class implement them, but unlike the interface you can have concrete methods that can be inherited but the logic is defined in the abstract class (as a default method ).

How do you create an abstract class?

We utilize the abstract keyword before the class. This will let the compiler know that this class is abstract, and cannot be instantiated using the new keyword.

Any method we want to implement in our sub class we prefix with the keyword abstract. This means that any sub class must declare it's own implementation and set the logic of this method.

public abstract class CoffeeBase 
{

}
Enter fullscreen mode Exit fullscreen mode

Why use an abstract class vs concrete class ?

Simple, flexibility. Abstract classes give the developer a bit more flexibility and freedom around what the class and methods can actually do when inherited. You can have the same method name, but a different implementation across multiple inheriting classes.

You can also use the virtual keyword. Ooooh new keyword!!

The virtual keyword allows you to define a default implementation (functionality) but allows the sub class to override the method functionality with it's own should it want to.

Let's move away from the Banking analogy (used in previous chapters) and move to a more simple example to explain this concept.

Abstract classes differ from interfaces in that you can write the method implementations within the abstract class itself. This is handy for common logic that will be shared across all sub classes.

Let's take the example of making a Coffee, and turn it into an abstract class.

public abstract class CoffeeBase {

    private readonly int sugar;
    private readonly int milk;
    private readonly int coffee;

    public CoffeeBase(int coffee, int sugar, int milk)
        this.coffee = coffee;
        this.milk = milk;
        this.sugar = sugar;
    }

    protected virtual void CreateMixer(){
        Console.WriteLine($"Created mixer of {coffee} coffee and {sugar} sugar");
    };

    public abstract void Prepare();
}
Enter fullscreen mode Exit fullscreen mode

We have created a CoffeeBase abstract class, that we must inherit from to create an instance of it. Let's build our sub classes that inherit from CoffeeBase.


public class IcedCoffee : CoffeeBase {

    public IcedCoffee (int coffee, int sugar, int milk) : base(coffee, sugar, milk)
    {

    }

    public override void Prepare(){

        CreateMixer();
        AddIce();
        PourInGlass();
        Console.WriteLine("Iced coffee is mixed, iced, and ready to go");
    }

    private void AddIce(){
        Console.WriteLine("Ice added to glass");
    }

    private void PourInGlass(){
        Console.WriteLine("Poured coffee mixed into a glass, ready to serve");
    }
}

public class CappucinoCoffee : CoffeeBase
{

    public CappucinoCoffee(int coffee, int sugar, int milk) : base(coffee, sugar, milk)
    {

    }

    public ovveride void Prepare(){

        CreateMixer();
        PourInMug();
        Console.WriteLine("Cappucino mixed and poured, ready to serve");
    }

    private void PourInMug(){
        Console.WriteLine("Pour mix into a mug");
    }
}
Enter fullscreen mode Exit fullscreen mode

You may be asking what does : base(coffee, sugar, milk) do? It simply passes the constructor parameters to the base (class you're inheriting from) constructor. In our case this will set the inherited fields of coffee, sugar and milk, so they can be used throughout our sub class.

It's that simple. Here you can see we've created two classes that inherit from a CoffeeBase, and have kept the default implementation of CreateMixer, as this will be the same for all coffee recipes really, as we're providing the quantities of each within the constructor.

We then override the abstract Prepare method, on the classes, this is required as they've been marked abstract and have no default implementation. The Prepare method is obviously abstract as every coffee needs to be prepared in a different way. This is the power that an abstract class gives you, every class has that method, but power of different implementation.

Let's see how this would be used in a console application, simply add all the classes to the project and import within the Program.cs as before and voila, a coffee maker.

To show the versatlity of it too, I created a MakeMyCoffee method that can take any object that derives from CoffeeBase and it will make the coffee the way it needs to be prepared, base on the implementation, and like interfaces thats the power of implementation and inheritance once again.

internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Time for Coffee");

            IcedCoffee coffee = new IcedCoffee(4, 2, 2);
            MakeMyCoffee(coffee);

            Console.ReadKey();
        }

        static void MakeMyCoffee(CoffeeBase baseCoffee)
        {
            baseCoffee.Prepare();
        }
    }

Enter fullscreen mode Exit fullscreen mode

Next Steps

Take what you've learnt over the last 3 chapters around inheritance, interfaces and abstract classes and build yourself a small console application encompassing these 3 skills.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .