C# Abstract Class and Interface

Summary: in this tutorial, you’ll learn when to use an abstract class and when to use an interface and the differences between an abstract class and an interface.

Choosing between an abstract class and an interface

In C#, an abstract class is similar to an interface. However, the abstract class and interface serve different purposes. Therefore, it’s important to know when to use an abstract class and when to use an interface.

Generally, when you want to model the is-a relationship and share a common implementation with all the subclasses, you use an abstract class.

But when you want to create a contract that other classes must adhere to, you use an interface.

The following example demonstrates how to use the abstract class Shape that has the Display() method shared by all of its subclasses:

abstract class Shape
{
    public abstract double GetArea();
    public abstract double GetPerimeter();

    public void Display()
    {
        Console.WriteLine($"Area: {GetArea():F2}, Perimeter: {GetPerimeter():F2}");
    }
}

class Rectangle : Shape
{
    readonly double length;
    readonly double width;

    public Rectangle(double length, double width)
    {
        this.length = length;
        this.width = width;
    }

    public override double GetArea()
    {
        return length * width;
    }

    public override double GetPerimeter()
    {
        return 2 * (length + width);
    }
}

class Circle : Shape
{
    readonly double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    public override double GetArea()
    {
        return Math.PI * radius * radius;
    }

    public override double GetPerimeter()
    {
        return 2 * Math.PI * radius;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var shapes = new List<Shape>()
        {
            new Rectangle(5, 10),
            new Circle(3)
        };

        foreach (var shape in shapes)
        {
            shape.Display();
        }

    }
}Code language: C# (cs)

Output:

Area: 50.00, Perimeter: 30.00
Area: 28.27, Perimeter: 18.85Code language: CSS (css)

In this example, the Shape class is an abstract class that defines two abstract methods GetArea and GetPerimeter. Any class that inherits from the Shape class needs to implement these methods.

The Shape class also has a concrete method Display that displays the area and perimeter of a shape. All the subclasses of the Shape class will share the same Display method.

The Rectangle and Circle classes extend the Shape class. Both of these classes share the same implementation of Display method, but they each provide their own implementation of GetArea and GetPerimeter methods.

The following example demonstrates how to use an interface as a contract:

interface ILogger
{
    void Log(string message);
}

class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

class FileLogger : ILogger
{
    private readonly string filePath;

    public FileLogger(string filePath)
    {
        this.filePath = filePath;
    }

    public void Log(string message)
    {
        using StreamWriter writer = new StreamWriter(filePath, true);
        writer.WriteLine(message);
    }
}

class Program
{
    static void Main(string[] args)
    {
        ILogger logger;

        if (args.Length > 0 && args[0] == "file")
        {
            logger = new FileLogger("log.txt");
        }
        else
        {
            logger = new ConsoleLogger();
        }

        logger.Log("Starting application...");

        // ... 

        logger.Log("Application stopped.");
    }
}
Code language: C# (cs)

In this example, we define the ILogger interface that has a single method Log. The Log method takes a string that represents the message to be logged.

The ConsoleLogger and FileLogger classes implement the ILogger interface and provide their own implementations of the Log method.

In the Main method, we create an instance of ConsoleLogger or FileLogger based on the command line argument.

If the argument is "file", we create a FileLogger object. Otherwise, we create a ConsoleLogger object.

Regardless of whether FileLogger or ConsoleLogger instance was created, we call the Log method to log some messages

Since both FileLogger and ConsoleLogger classes implement the same interface, we can easily switch between them at runtime.

Abstract class vs. Interface

The following table illustrates the differences between abstract classes and interfaces:

Abstract ClassesInterfaces
Shared implementationDefine a contract
Can only inherit from a single base classCan implement any number of interfaces
Unconstrainted implementation codeLimited implementation code
Can have automatic propertiesNo automatic properties

Summary

  • Use an abstract class to model an is-a relationship and share a common implementation with all subclasses.
  • Use an interface to represent a contract to which other classes must adhere.
Was this tutorial helpful ?