In this article, we will learn about default interface methods in C#.

To download the source code for this article, you can visit our GitHub repository.

So, let’s start.

Introduction to a Default Interface Method

The versions before C#8.0 did not allow an implementation code inside interfaces. From C#8.0 we have a default interface method feature that enables us to have a member’s default implementation inside an interface. It is also called the virtual extension method.

So, let’s start with an ICalendar interface:

public interface ICalendar
{
    public DateTime Date { get; set; }

    public string ShowMessage()
    {
        return "Default Calendar";
    }
}

In this interface, we can see a ShowMessage method with its body. This is known as the default interface method.

Key Points to Remember

Let’s see what are the key points that we have to keep in our mind when we work with default interface methods:

  • We can declare static fields, methods, properties, or events
  • We can have a body of methods, indexers, properties, and events
  • We can’t instantiate fields or properties

Access Modifiers in a Default Interface Method in C#

Explicit access modifiers are accessible to everyone by default. Those can be public, private, protected, sealed, static, virtual, extern, abstract, and internal. Any member with its implementation inside the interface is a virtual member so that deriving interface can override them. The sealed or private access modifiers prevent this. 

Any member without its implementation and only declaration are abstract. We can’t have explicit access modifiers in the overridden method.

When to Use a Default Interface Method in C#

Before C# 8.0, an interface contained only declarations of the members, and once implemented in any child class, we couldn’t modify it. From C#8.0, if we want to add a new functionality/method to the existing interface and we also want our existing code to continue to work without breaking the implementation of interfaces in the production, we can use default interface methods. It provides the traits concept of OOPS (Object-Oriented Programming System).

Let’s understand this with an example.

In production, we have a code where the IYearCalendar interface derives from the previous ICalendar interface. We can add new methods IsLeapYear and ShowMessage inside IYearCalendar without breaking the existing code in production:

public interface IYearCalendar : ICalendar
{
    public bool IsLeapYear()
    {
        if (Date.Year % 400 == 0)
            return true;
        if (Date.Year % 100 == 0)
            return false;
        if (Date.Year % 4 == 0)
            return true;

        return false;
    }

    string ICalendar.ShowMessage()
    {
        if (IsLeapYear())
            return $"{Date} is a leap year";
        else
            return $"{Date} is not a leap year";
    }
}

These methods act as default interface methods,IsLeapYear checks if a given year is a leap year or not in the interface only and ShowMessage displays the output. 

Handles a Diamond Problem or an Ambiguity Problem in Multiple Inheritance 

When any class inherits two or more interfaces, and these interfaces have members with the same names, then the compiler can’t decide the preference among the same named methods. This scenario in multiple inheritances in interfaces poses an ambiguity problem known as the diamond problem.  

To solve this problem, from C#8.0 interface methods are implemented in child classes or the caller passes the state as an argument.

Let’s continue with the example…

Wanna join Code Maze Team, help us produce more awesome .NET/C# content and get paid? >> JOIN US! <<

We have another interface called IMonthCalendar which inherits the same ICalendar  interface, which IYearCalendar inherits from:

public interface IMonthCalendar : ICalendar
{
    public bool Is31DaysMonth()
    {
        if (DateTime.DaysInMonth(Date.Year, Date.Month) > 30)
            return true;

        return false;
    }

    string ICalendar.ShowMessage()
    {
        if (Is31DaysMonth())
            return $"{Date} has 31 days";
        else
            return $"{Date} does not has 31 days";
    }
}

This interface contains the Is31DaysMonth method, which checks if the given month has 31 days or not and ShowMessage displays the output.

Now, we can create the MyCalendar class that inherits both IYearCalendar and IMonthCalendar interfaces:

public class MyCalendar : IYearCalendar, IMonthCalendar
{
    public DateTime Date { get; set; }

    DateTime ICalendar.Date { get => Date; set => Date = value; }
}

As we compile this code, we will get an error:

error using a default interface method with multiple inheritances in c#

We get this becauseShowMessage does not have the most specific implementation. Neither IYearCalendar.ShowMessage nor IMonthCalendar.ShowMessage are the most specific. So the compiler doesn’t know which method needs to be implemented.

Hence, we can solve this by implementing ShowMessage in the  MyCalendarchild class:

public string ShowMessage()
{
    return $"Today is {Date}";
}

This class implementation overrides the default implementation of the interface and the compiler uses this class implementation of the ShowMessagemethod.

Wanna join Code Maze Team, help us produce more awesome .NET/C# content and get paid? >> JOIN US! <<

Conclusion

For a good design, we can use the default interface method but we need to be careful in the real-world implementation of this feature. It should not violate the Single Responsibility Principle of the SOLID principles.