What are Default Interface Methods in C# 8.0?

hackernoon.com hackernoon.com3 months ago in #Dev Love47

@miguel-bernard Miguel is passionate about teaching, developers’ communities and everything related to .Net. Introduction Last September, Microsoft released a bunch of new features with C# 8.0. One of the major features that they introduced is Default interface methods. In this post, we’ll explain what they are, show a few examples and provide guidance to avoid common pitfalls. What are default interface methods in C# 8.0 Interfaces in C# used to be definitions or contracts without any behavior. This assumption is no longer true. Now, with default interface methods, you can define a default body for any interface methods. When you do so, they mostly behave like virtual methods in an abstract class. Why are default interface methods important If they behave like virtual methods on an abstract class, why not simply use an abstract class? There is one big difference that justifies their existence: In C#, you can only inherit from one base class, but you can implement as many interfaces as you want. That subtle point makes all the difference when it comes to code reuse, a.k.a. the Don’t Repeat Yourself (DRY) principle. That new feature will enable many scenarios that were previously difficult or impossible to achieve. e.g. Extend interfaces safely by adding methods with implementations Create parameterized implementations to provide greater flexibility Enable implementers to provide a more specific implementation in the form of an override Real examples of default interface methods Without default interface methods internal interface ILogger { void WriteCore(LogLevel level, string message); void WriteInformation(string message); void WriteWarning(string message); void WriteError(string message); } internal class ConsoleLogger : ILogger { public void WriteCore(LogLevel level, string message) { Console.WriteLine($”{level}: {message}”); } public void WriteInformation(string message) { this.WriteCore(LogLevel.Information, message); } public void WriteWarning(string message) { this.WriteCore(LogLevel.Warning, message); } public void WriteError(string message) { this.WriteCore(LogLevel.Error, message); } } internal class TraceLogger : ILogger { public void WriteCore(LogLevel level, string message) { switch (level) { case LogLevel.Information: Trace.TraceInformation(message); break; case LogLevel.Warning: Trace.TraceWarning(message); break; case LogLevel.Error: Trace.TraceError(message); break; } } public void WriteInformation(string message) { this.WriteCore(LogLevel.Information, message); } public void WriteWarning(string message) { this.WriteCore(LogLevel.Warning, message); } public void WriteError(string message) { this.WriteCore(LogLevel.Error, message); } } For every implementation of `ILogger` we have to repeat this same code block: public void WriteInformation(string message) { this.WriteCore(LogLevel.Information, message); } public void WriteWarning(string message) { this.WriteCore(LogLevel.Warning, message); } public void WriteError(string message) { this.WriteCore(LogLevel.Error, message); } With default interface methods internal interface ILogger { void WriteCore(LogLevel level, string message); void WriteInformation(string message) { this.WriteCore(LogLevel.Information, message); } void WriteWarning(string message) { this.WriteCore(LogLevel.Warning, message); } void WriteError(string message) { this.WriteCore(LogLevel.Error, message); } } internal class ConsoleLogger : ILogger { public void WriteCore(LogLevel level, string message) { Console.WriteLine($”{level}: {message}”); } } internal class TraceLogger : ILogger { public void WriteCore(LogLevel level, string message) { switch (level) { case LogLevel.Information: Trace.TraceInformation(message); break; case LogLevel.Warning: Trace.TraceWarning(message); break; case LogLevel.Error: Trace.TraceError(message); break; } } } Haaaaa… this is much better. Now the implementations only contain the relevant code. Pitfall to avoid As much as multiple inheritance is awesome, you also need to be very mindful of the dreaded diamond problem. It’s an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If there is a method in A that B and C have overridden,  » Read More

Like to keep reading?

This article first appeared on hackernoon.com. If you'd like to keep reading, follow the white rabbit.

View Full Article

Leave a Reply