C# Interface with Default Implementation

Summary: in this tutorial, you’ll learn how to define a C# interface with a default implementation in C# 8 or later.

Introduction to the C# interface default implementation

Suppose we have an interface ILogger that has one method Log() and two classes ConsoleLogger and FileLogger that implements the interface:

interface ILogger
{
    void Log(string message);
}

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

class FileLogger : ILogger
{
    public string Filename { get; set; }

    public FileLogger(string filename)
    {
        Filename = filename;
    }
    public void Log(string message)
    {
        File.AppendAllText(Filename, message + Environment.NewLine);
    }

}Code language: C# (cs)

If you want to add a new method to the ILogger interface, your program won’t work unless you implement this method in all the existing classes that implement the interface.

For example:

interface ILogger
{
    void Log(string message);
    void Log(Exception exception);
}Code language: C# (cs)

In this example, we add a new Log() method that has a parameter as an instance of the Exception class. Howeer, this will break the program.

To resolve this, C# 8 or later allows the interface members to have default implementations.

For example, a method of the interface can have a default implementation. If the implementing classes do not provide an implementation for that member, no error will occur. Instead, the default implementation is used.

The following example adds the Log() method that accepts an exception instance. This Log() method has an implementation that shows the exception message to the console:

interface ILogger
{
    void Log(string message);
    void Log(Exception exception) => Console.WriteLine(exception.Message);
}Code language: C# (cs)

In this case, the existing classes don’t need to implement the new Log() method. Hence, the program will compile successfully and work properly.

The following creates a new instance of the FileLogger class and calls the Log() method with an exception argument:

// Program.cs
ILogger logger = new FileLogger(@"C:\temp\logs.txt");
logger.Log(new Exception("An exception occurred."));Code language: C# (cs)

Because the FileLogger doesn’t implement the new Log() method, the new Log() method uses the default implementation in the interface to show the exception message to the console.

The following FileLogger class provides an implementation for the new Log() method:

class FileLogger : ILogger
{
    public string Filename { get; set; }

    public FileLogger(string filename)
    {
        Filename = filename;
    }
    public void Log(string message)
    {
        File.AppendAllText(Filename, message + Environment.NewLine);
    }

    public void Log(Exception exception)
    {
        Log(exception.Message);
    }

}Code language: C# (cs)

If you run the program, you’ll see that it logs the exception message to a log file:

// Program.cs
ILogger logger = new FileLogger(@"C:\temp\logs.txt");
logger.Log(new Exception("An exception occurred."));Code language: C# (cs)

Put it all together.

interface ILogger
{
    void Log(string message);
    void Log(Exception exception) => Console.WriteLine(exception.Message);
}

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

class FileLogger : ILogger
{
    public string Filename { get; set; }

    public FileLogger(string filename)
    {
        Filename = filename;
    }
    public void Log(string message)
    {
        File.AppendAllText(Filename, message + Environment.NewLine);
    }

    public void Log(Exception exception)
    {
        Log(exception.Message);
    }

}Code language: C# (cs)

Summary

  • Provide default implementation for an interface member to keep the program backward compatibility.
Was this tutorial helpful ?