Interfaces in C# 8.0 Get a Makeover

For years, in C#, an interface strictly follows the I only rule: Here, an interface basically says what a class should do, not how it should do it. Such abrupt clarity would make interfaces easily reason about, but it would also expose long-term problems when trying to evolve libraries forward. C# 8.0 changed this by including default implementations in interfaces. At a superficial level, this looks rather as an innocent change, but the internal implications for API design and versioning, and how we, as developers, observe abstraction, hold profound insights.

What Changed in C# 8.0 Interfaces

Before C# 8.0, adding a new member to an interface was a breaking change. Every class that implemented the interface had to be updated, even if the new method was optional or had an obvious default behaviour. This made interface evolution risky, especially for widely used libraries.

What Are Default Interface Implementations?

A default interface implementation is a method defined in an interface with an actual body, not just a signature. When a class implements that interface, it automatically inherits the default behaviour unless it chooses to provide its own version.

This means interfaces can grow over time without forcing immediate changes across all implementations. For library authors, this removes pressure to lock interfaces permanently once they are released, while still preserving backward compatibility.

Why the Language Needed This Change

The primary motivation was versioning. Large frameworks such as ASP.NET Core middleware, often avoid improving interfaces because the cost of breaking existing implementations is too high. Over time, this leads to awkward design patterns, duplicated interfaces, or bloated abstractions.

By allowing default implementations, C# enables incremental improvement. New members can be added safely, existing code continues to work, and consumers can adopt new behaviour at their own pace. This makes long-lived APIs easier to maintain and evolve responsibly.

How Interfaces Still Differ from Abstract Classes

Although interfaces can now contain behaviour, they are not interchangeable with abstract classes. Interfaces still cannot have fields or constructors, and they focus on defining capabilities rather than shared state.

Another key difference is inheritance. A class can implement multiple interfaces but only inherit from one abstract class. Default implementations do not change this rule. Understanding these boundaries helps avoid misuse and keeps designs intentional rather than accidental.

Design Implications and Trade-Offs

Introducing behaviour into an interface has a subtle effect on behavior-responsibility in the codebase. While the feature promises to resolve tangible problems, it also adds complexity that has to be managed by developers carefully.

The crucial question is not merely if default implementations should be used, but when one should use them.

Improved Interface Versioning

Design Implications

From a versioning perspective, default implementations are a clear win. Interfaces can be extended without forcing breaking changes, which reduces upgrade friction for consumers.

That said, defaults should be chosen carefully. A poorly chosen implementation can lock in behaviour that is hard to change later. Defaults should be simple, predictable, and broadly applicable, rather than opinionated or context-specific.

Conflicts with Multiple Interfaces

If a class implements multiple interfaces that define the same method with default implementations, the compiler requires the class to resolve the conflict explicitly. This prevents silent ambiguity but adds another design consideration.

While this situation is relatively rare, it highlights the importance of understanding how multiple interface inheritance works in modern C#. The language provides safeguards, but developers must still make clear decisions.

When Default Implementations Are the Wrong Choice

Default implementations are not a replacement for good abstraction. If shared behaviour is central to a concept and relies on internal state, an abstract class may still be the better option.

Interfaces should remain focused on defining what a type can do. Using default implementations primarily for compatibility or small, self-contained behaviour helps avoid turning interfaces into informal base classes.

What This Means for Existing Codebases

For teams maintaining established systems, including those working with ASP.NET Core applications, default interface implementations offer both relief and responsibility. Libraries can evolve more safely, but developers must be aware that interfaces may now contain logic. This can surprise those who assume interfaces are purely declarative.

A Small Syntax Change with Big Design Consequences

Default values in interfaces might seem like an insignificant add-on, but they address a long-standing limitation in C#. In .NET, they permit interfaces to evolve while still ensuring backwards compatibility, essential features for a modern library. The real beauty of the feature is not so much in injecting logic everywhere as in understanding that sometimes less is more in terms of design.