C# 8.0 : Default Implementation of Interface Members

The way C# language is evolving is definitely exciting and one feature that truly makes the wait for C# 8.0 all the more exciting is default implementation of interfaces.

Since the onset, interfaces were behaviors characterized by member signatures, strictly disallowing any implementation. All that is going to change when C# 8.0 rolls out, as it supposedly, plans to support default implementation of interfaces. What it means is further trimming down the walls between interface and class (but remember, walls would still exist : Interface and Classes will still have significant difference, we will talk about it a bit later.)

Let’s look at an example, courtesy MSDN Blogs.

interface ILogger
{
void Log(LogLevel level, string message);
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Log(Exception) gets default implementation
}

Doesn’t that look exciting ? Gone are days when as an Author of an interface, you were almost handicapped extending the interface fearing it might break any of implementing code. You could now provide the default implementation of your interface member, and still be assured that all implementing code works as fine as it was before.

If this doesn’t excite you, think from another perspective. What this implies is possibility of multiple inheritance (almost) in a language that was never really supported it. You could now provide two interfaces with default implementation, and have a class inherit from both.

interface IParentA
{
string MethodA() => nameof(MethodA);
}

interface IParentB
{
string MethodB() => nameof(MethodB);
}

class Child : IParentA,IParentB
{
void MethodChild()
{
Console.WriteLine($"{nameof(MethodA)} = {MethodA()}");
Console.WriteLine($"{nameof(MethodB)} = {MethodB()}");
}
}

This effectively brings in Multiple Inheritance in place. What would be interesting to see is how Microsoft circumvented the Diamond Problem. Let’s delve a bit into Diamond Problem first, before trying to understand Microsoft’s workaround from what ever we know as of now.

Diamond Problem

Let’s consider the following inheritance structure assuming C# were to support Multiple Inheritance.

class A
{
public virtual string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

class B : A
{
public override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

class C : A
{
public override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// Let's assume this works for sake of demonstration
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

In the above code, class D inherits from both Class B and Class C, both of which, in-turns inherits from Class A and overrides virtual member DemoMethod. Assuming this code compiles, what would be the output of Print() Method in Class D. Will it invoke method from Class B or Class C ? This ambiguity is better known as Diamond Problem.

Microsoft intents to work around the Diamond Problem here by taking the most specific override at the time. As Microsoft pens it down

“Diamonds with Classes

A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of that member at all.”

Let’s rewrite the above code using default implementation of interfaces.

interface A
{
string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

interface B: A
{
override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

interface C: A
{
override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// This would be Error as no most specific override for A.DemoMethod()
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

// This would be Error as no most specific override for A.DemoMethod()
class E:B,C,A
{
string DemoMethod()=> B.base.DemoMethod();
public void Print()=> Console.WriteLine(DemoMethod());
}

So will now abstract class == interface ?

No, not really. Even though the differences are diminishing, there still lies a lot of difference between the two. To name a few
– Unlike Abstract Class ,you still can’t write a default constructor for interface.
– Covariance and Contravariance

Essentially, abstract class and interface would continue to coexist in its own space. What Microsoft has done with Default Implementation of Interface is making use of Traits (more on traits later.)

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s