Default implementations in interfaces
With last week’s posts Announcing .NET Core 3.0 Preview 5 and Visual Studio 2019 version 16.1 Preview 3, the last major feature of C# 8.0 is now available in preview.
A big impediment to software evolution has been the fact that you couldn’t add new members to a public interface. You would break existing implementers of the interface; after all they would have no implementation for the new member!
Default implementations help with that. An interface member can now be specified with a code body, and if an implementing class or struct does not provide an implementation of that member, no error occurs. Instead, the default implementation is used.
Let’s say that we offer the following interface:
interface ILogger { void Log(LogLevel level, string message); }
An existing class, maybe in a different code base with different owners, implements ILogger
:
class ConsoleLogger : ILogger { public void Log(LogLevel level, string message) { ... } }
Now we want to add another overload of the Log
method to the interface. We can do that without breaking the existing implementation by providing a default implementation – a method body:
interface ILogger { void Log(LogLevel level, string message); void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); }
The ConsoleLogger
still satisfies the contract provided by the interface: if it is converted to the interface and the new Log method is called it will work just fine: the interface’s default implementation is just called:
public static void LogException(ConsoleLogger logger, Exception ex) { ILogger ilogger = logger; // Converting to interface ilogger.Log(ex); // Calling new Log overload }
Of course an implementing class that does know about the new member is free to implement it in its own way. In that case, the default implementation is just ignored.
The best way to get acquainted with default implementations is the Tutorial: Update interfaces with default interface members in C# 8 on Microsoft Docs.
Happy hacking!
Mads
65 comments