C#  

Understanding Interfaces in C#: Why Default Interface Implementations Exist Alongside Abstract Classes

Introduction:

Interfaces are one of the most powerful building blocks in Object-Oriented Programming (OOP) and Clean Architecture. Whether you work in ASP.NET Core APIs, Microservices, Desktop (WPF/WinForms) applications, MAUI mobile apps, or enterprise cloud systems, you will interact with interfaces daily — often without even realizing it.

This article explains:

  • What an interface is

  • Why interfaces are important

  • How they are used in real projects

  • Why Microsoft added default interface implementation in C# 8, even though abstract classes already existed

  • And how this feature solves real enterprise-scale problems

What Is an Interface?

An interface defines a contract — a set of methods, properties, events, or indexers that a class must implement.

  •  Key Idea:

  • Interface = Contract

  • Class     = Implementation

An interface defines what a class should do — but not how the work will be done.
The implementation details are always provided by the class that implements the interface.

Why Do We Need Interfaces?

Interfaces solve two major software engineering challenges:

Dependency Management

Without interfaces:

  • Classes become tightly coupled

  • Changing a single implementation may break the entire system

With interfaces:

  • Code depends on abstractions, not concrete classes

  • The system becomes scalable, testable, extendable, and loosely coupled

This is the foundation of Dependency Injection (DI) and the Dependency Inversion Principle (SOLID – D).

Example:

public interface ILoggerService
{
    void Log(string message);

    void Error(string error);
}

Implementation

public class FileLogger : ILoggerService
{
    public void Log(string message)
    {
        Console.WriteLine($"[File Log] {message}");
    }
 public void Error(string error)
    {
        Console.WriteLine($"[File Error] {error}");
    }
}

Usage in ASP.NET Core DI:

services.AddScoped<ILoggerService, FileLogger>();

public class HomeController : Controller
{
    private readonly ILoggerService _logger;

    public HomeController(ILoggerService logger)
    {
        _logger = logger;
    }

    public IActionResult Index()
    {
        _logger.Log("Home Page Loaded");
        return View();
    }
}

✔ The controller does not care which logger is being used
✔ Tomorrow you can replace FileLogger → DatabaseLogger → CloudLogger without changing any controller code.

The Real Problem Before C# 8

Abstract classes already offered:

✔ shared logic
✔ inheritance
✔ structure

…but they failed in one critical scenario:

Updating a shared interface in live enterprise systems broke every application that implemented it

Example:

public interface IPaymentService
{
    bool Pay();
}
// One year later…

bool Refund(); // <-- Adding this breaks all implementing classes

If that interface was part of:

  • A NuGet package

  • A shared SDK

  • A microservice contract

…then every consumer application using that interface would fail to compile.

Imagine 500+ production systems across multiple cities depending on the same interface.

Updating all of them overnight = impossible, expensive, high-risk

Microsoft Was Responding to Real-World Industry Needs:

✔ .NET had become the foundation of global enterprise systems
✔ Microservices increasingly shared interface-based contracts
✔ NuGet libraries needed safe evolution
✔ Companies needed gradual migration, not forced rewrites
✔ Backward compatibility became critical

In short — software had to grow without breaking the world around it.

C# 8 Solution: Default Interface Implementation

C# 8 introduced a new capability — interfaces can now contain optional method bodies.

public interface IPaymentService
{
    bool Pay();
    public bool Refund(decimal amount) // Default implementation
    {
        return false; // Safe fallback
    }
}

What Does This Change Enable?

Benefit Explanation

  • Existing apps continue working No compile-time errors

  • New apps may override the method Custom logic only if needed

  • NuGet packages evolve safely Add new API surface without breaking clients

  • Migration becomes optional Gradual modernization

Conclusion:

Microsoft introduced default interface implementation because enterprise-scale software needed a way to evolve shared interfaces without breaking dependent applications.

Abstract classes could not solve:

  • backward compatibility

  • interface contract evolution

  • multi-inheritance behaviour

  • safety across distributed systems

Default interface methods make .NET:

  • future-ready

  • backward compatible

  • more flexible for microservices and NuGet libraries

  • and significantly easier to maintain in large organizations

In simple terms — default interface implementation allows software to grow safely, instead of breaking everything when a new feature is needed.