29

C# 8: Default implementations in interfaces

 4 years ago
source link: https://www.tuicool.com/articles/2UriIvv
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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.

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.

eaqYr2m.jpg!web

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 aboutthrow 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.

M3IZZbV.jpg!web

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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK