Continuing to examine new C# 8 features, today we are taking a look at a bit controversial one. It’s a possibility to provide default interface methods implementation.
How will this possibility change the way we write C#? Why is it being introduced? I’ll try to address these questions today 🙂
Interfaces today
As we all know from the current version of C#, interfaces allow to define a kind of contract, which must be respected by a class implementing it. Today interfaces can only contain definitions of methods (with no body). These methods are then actually implemented by concrete classes. If a class doesn’t implement all of its interface’s methods, compiler produces an error.
What’s problematic in this approach? Assuming that you have the following interface and class(es) implementing it:
With time, you may want to extend IDeveloper interface by adding a new method, for instance:
Now you have a problem, because the new method must be implemented in all places where the interface was used. Compiler gives you an error as long as it’s not done:
If your interface is used in many places, also by another teams or developers (which is often the case in reality), this is a breaking change 🙁
It may be really painful, depending on how extensively your interface is used. In any case, all classes based on the IDeveloper interface must implement the new LearnNewLanguage(string language) method. Only then the code compiles.
One of the reasons for introducing into C# default interface methods is to avoid such breaking changes scenarios 🙂
Default Interface Methods
As we can read in the feature’s design notes, an inspiration for it is a similar concept already alive in Java – Default Methods.
The main idea of this new C# feature is a possibility to provide a default implementation of an interface’s method.
Realization in C# 8.0
In our example using C# 8.0 we’ll be able to implement it as follows:
In that case, our class implementing the IDeveloper interface can legally not implement this new method and there will be no compilation error:
Notice that it only works when BackendDev is contextually treated as IDeveloper. It means that a class doesn’t inherit members from the interfaces it implements:
What about multiple inheritance?
As some of you probably realized, this new feature exposes C# to a multiple inheritance problem (also known as Diamond of Death). So far C# didn’t have this issue, because we can only inherit from a single class. We can implement multiple interfaces, but so far the interfaces couldn’t provide methods’ implementation. Because of that, there was no Diamond of Death possible. Soon it’s gonna change 🙂
Diamond of Death in C# 8
In case of C# default interface methods we may illustrate this issue with the following code:
Please note that the syntax or places in which compiler generates errors may still change in the final feature’s version.
To better illustrate this Diamond of Death example, take a look at the following diagram:
The question is: which method should be called in such case?
Solution: most specific override rule
C# 8.0 will solve this issue by introducing the most specific override rule. It means that in our case illustrated above, the compiler will issue an error and won’t let us do this.
To avoid this ambiguity, we’ll be forced to implement or override LearnNewLanguage(string language) in IFullStackDev interface. Only then it will be considered the most specific version of the method in the interfaces hierarchy we have. It will then be used when calling the method on objects contextually treated as instances of IFullStackDev. We could also implement it directly in the Dev class and use the class (not interface) type. It would then call the most specific method from the class. This way multiple inheritance issue will be eliminated.
Compiler for the rescue
There are a lot of other potential ambiguity scenarios that may take place after introducing into C# default interface methods. You can check all of them with potential solutions well-discussed on GitHub.
The general assumption is that all code which may lead to issues related to default interface methods implementations is detected by the compiler. In effect, we should get appropriate errors when compiling the source code. That way, programmer can fix all potential conflicts directly when these arise.
What about .NET Framework?
What’s interesting with default interface methods is that it requres changes in the runtime. According to current assumptions, it means that this feature will not work with .NET Framework! Only CoreCLR and Mono stack runtimes are going to receive these new updates.
Summary
As the motivation for introducing into C# default interface methods the language team members mention possibility to interact with some Android and iOS APIs, which already support such feature. They also claim that C# will get the traits programming language feature with default interface methods.
In my opinion this is the most controversial from all C# 8.0 new features. It completely changes something which was obviousness for the developers for years. Probably we’ll see less breaking changes issues when the interfaces are extended, but looking on the list of issues and edge cases it produces… Maybe assuming it already works in a similar way in Java we should just accept it? 🤔
Another interesting, .NET-specific aspect is that this feature will not work with .NET Framework. It’s probably the very first moment when the difference in pace between .NET Core and Framework is so explicit.
I’m also very curious about the performance implications of default interface methods. I’ll for sure be following this issue on GitHub to see how it evolves 🙂
What do you think? Is it a good idea to introduce default interface methods into C#?