C# 8: Default implementations in interfaces

C# 8.0 will introduce new language feature – default implementations of interface members. It means that we can define body to interface member and implementing class that doesn’t implement given interface member will use default one from interface itself. Here’s my deep-dive and analyzis to default implementions of interfaces.

  1. C# 8: Default implementations in interfaces
  2. Using-declarations in C# 8.0

Default implementations of interface members

Let’s start with classic example based on Mads Torgersen blog post Default implementations in interfaces and take a look at famous logger example. Let there be interface for logger.

public interface ILogger
{
    void Info(string message);
    void Error(string message);
}

When building logger classes we have to implement all members defined by interface.

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

    public void Info(string message)
    {
        Console.WriteLine(message);
    }
}

Suppose now we have to extend ILogger interface with new member and we don’t want to affect any existing class with this change. This is how we define interface with current version of C# and we are stuck.

public interface ILogger
{
    void Info(string message);
    void Error(string message);

    // New method
    void Warn(string message);
}

In C# 8.0 we can solve the problem by providing implementation to Warn() method.

public interface ILogger
{
    void Info(string message);
    void Error(string message);

    // New method
    void Warn(string message)
    {
        Debug.WriteLine(message);
    }
}

It builds without errors. Let’s try this code out with simple test.

Default implementation of interface

So, the interface method works as expected and ConsoleLogger class is not affected by change in interface.

If we implement Warn() method in ConsoleLogger class then Warn() method of class is used.

What happens internally?

Every new feature in C# language is always worth investigating also beyond compiler. Some language features are illusion – compiler turns these to typical simple code like the feature never existed. Take a look at my writing about throw expressions in C# 7.0.

To sew what compiler produced I make default implementation of Warn() method use Console instead of Debug as otherwise we get empty method body when building the code in Release mode. Another possible source of differences may come in from fact that we have method with body in our interface. Let’s define also classic ILogger interface to compare them.

public interface ILoggerClassic
{
    void Info(string message);
    void Error(string message);
    void Warn(string message);
}

After compiling the code and investigating the result in disassembler we can see the difference in Intermediate Language level.

Interface method with body vs classic interface method

Interesting thing is that interface method with body is missing abstract keyword. From every other aspect it is defined exactly the same way as classic interface method.

This fact makes me ask one thing – isn’t this feature been available at IL level for long time? Is it possible it made will make its way to C# years later? As surprising as it may be – the answer is yes. Years ago when I blogged about how to throw and catch strings in IL I went through IL book by Vijay Mukhi. Based on this book and Expert .NET 2.0 IL Assembler by Serge Lidin I was able to build program in IL where interface method had body. Of course, it wasn’t possible to use it in C# directly but it was possible through reflection. The book by Serge Lidin details extremely well out internals of .NET runtime and it was kind of horrible for me to see how many responsibilities are actually set to language compilers. So, yes, this feature has existed in IL level for years.

Do we really need default implementations?

The need for default implementations of interface members is similar hot topic for argument like local functions in C# 7.0. I mean we have survived seven versions of C# without default implementations. What’s the point of default implementations then?

Some developers may feel specially bad about default implementations because technically they are correct and maybe even convenient but they distort planned functionality of implementing classes. Sample code above illustrates the problem well. Signature of interface is changed and with this all implementing classes have suddenly some new functionalities injected. We can write debug logger and make Warn() method of ILogger to use Console as output. But what has debug logger to do with Console? And even worse – what if default implementation conflicts with something in implementing class? Another question – why not abstract class with default implementations?

Default implementations may actually turn out to be very useful. Suppose we have public library used by other developers all around the world. We want to release new version where we have improved some interfaces and – of course – we want to avoid breaking changes. As we have been on interfaces with our library for God knows how long then we cannot just jump between interfaces and abstract classes like we wish. If we add new members to our interfaces then we can define implementations so programs using previous versions of our libraries can use also new versions without changes to their codebase. I guess for many developers it makes life way easier.

I made an experiment. I wrote program, one library with ILogger interface and another with ConsoleLogger class. I built my program and published it to other disk on my machine. I ran application to see if it works. Everything was fine. Then I added new Warn() method with default implementation to ILogger. I build only ILogger library and replaced it in folder where I published my program. I ran my program again and it still worked although interface was changed.

Wrapping up

Default implementations is powerful language feature coming to C# 8.0. Although it may seem dangerous for some developers then others will certainly be happy with it. Those who are writing libraries and components for public use may find default implementations specially useful as they let us avoid breaking changes in interfaces.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    11 thoughts on “C# 8: Default implementations in interfaces

    • May 21, 2019 at 8:25 pm
      Permalink

      A good summary, thank you!

    • May 27, 2019 at 2:04 pm
      Permalink

      Oh boy.. I see the advantages of this, but in practical terms, it will allow people to mess a lot in current projects.

    • May 28, 2019 at 9:23 am
      Permalink

      I think this is feature for those who build API-s and these guys should know very well when to use this feature.

    • August 6, 2019 at 4:24 pm
      Permalink

      Thanks for a great article. I wonder how it will work when we have implementations in all three places (interface, abstract class and inheritance class). How will it work or is it possible to build such a structure that all three implementations will execute?

    • August 6, 2019 at 8:19 pm
      Permalink

      Yes, it’s possible. This is how I made it build. With Preview 7 it crashes IIS and Kestrel with error 0xc0000005 (Access violation). I created new issue to Github for this: https://github.com/dotnet/csharplang/issues/2714

      Notice how I had to cast AbstractDummy to interface to call SayHello() defined as default implementation in interface.

      public interface IDummy
      {
          void SayHello()
          {
              Debug.WriteLine("Hello");
          }
      }
      
      public abstract class AbstractDummy : IDummy
      {
          public virtual void SayHello()
          {
              ((IDummy)this).SayHello();
          }
      }
      
      public class Dummy : AbstractDummy
      {
          public override void SayHello()
          {
              base.SayHello();
          }
      }
      
    • October 28, 2019 at 12:49 am
      Permalink

      I am very confused…
      They are really going to change a very basic and fundamental concept of OOP!!!!
      Why should an interface has default implementation for method?????
      In OOP an interface is a contract for class that assigns structure of it.
      In fact because of this reason that interface doesn’t have implementation, classes can implement(inherit) from multiple interfaces.
      This is very mess….
      With this feature SOLID principles are going to die and Abstract class will be inefficient….

    • January 30, 2020 at 7:31 pm
      Permalink

      I undersrand world is evolving with many open API’s…But I wonder how many developpers on the planet really need this new feature. Now it’s MS choice to open this door and close one of the biggest OOP principles. It’s like letting every developer patch his own compiler feature to make his C# environnement work as he wants. What a mess…!

    • January 30, 2020 at 8:45 pm
      Permalink

      This feature was introduced mostly for component and package builders so they can introduce new features with smaller amount of breaking changes to existing systems. Suppose that new version of some package has important security or stability fixes and also some new features that needed update to some existing interface. With default implementations of interface members the new package can be just updated without need to change anything in existing system. Anyway there’s no code that calls new interface members.

      Downside of this feature is myriad of different misuses by novice or careless programmers. It’s easy to abuse with big solution containing many projects. I can already imagine how developers in hurry make small updates to interfaces and say it doesn’t break anything. Later somebody has to solve all this mess.

    • April 30, 2021 at 2:51 pm
      Permalink

      I very much agree this was a very bad idea.

      And here is another fundamental problem you are implementing multiple inheritance via interfaces.
      And not one mention in any of these articles about collision rules as if it matters cause its gonna happen.
      Most important part of this is missing.

      interface A(){ M(){ /*a*/ }}
      interface B(){ M(){ /*b*/ }}

      public class C : A, B
      {
      // what default am i calling?
      public void Test(){ M(); }
      }

    • April 30, 2021 at 2:56 pm
      Permalink

      Id like to point out above if you don’t get it that previously when there was no implementation which contract that was picked didn’t matter because they were effectively equivalent.

      Now they are effectively a version and it does matter which one is used.

    Leave a Reply

    Your email address will not be published. Required fields are marked *