C# Interfaces Explained: Build Flexible and Maintainable Applications

Interfaces are one of the core features of C# for creating flexible software designs. They define a contract that classes can implement, making it easier to build reusable, testable, and loosely coupled applications.

What is an Interface in C#?

An interface defines a set of members that implementing types agree to provide. It describes what a type can do without requiring the interface to define how the operation is implemented.

CSHARP
interface IAnimal
{
    void MakeSound();
}

class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Woof");
    }
}

Why Use Interfaces?

  • Create clear contracts between components
  • Reduce tight coupling
  • Support multiple implementations
  • Make code easier to test
  • Improve application flexibility
  • Support dependency injection
  • Enable polymorphic behavior

Implementing an Interface

A class implements an interface by using the colon syntax and providing the required members.

CSHARP
interface ILogger
{
    void Log(string message);
}

class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

ILogger logger = new ConsoleLogger();
logger.Log("Application started");

Interface Naming Convention

By convention, C# interface names commonly begin with the letter I. This makes interfaces easy to identify when reading code.

CSHARP
interface IRepository
{
}

interface IPaymentService
{
}

interface INotificationService
{
}

Interfaces with Properties

Interfaces can define properties that implementing classes must provide.

CSHARP
interface IUser
{
    int Id { get; }
    string Name { get; }
}

class User : IUser
{
    public int Id { get; }
    public string Name { get; }

    public User(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

Interfaces with Methods

Interfaces can define methods that represent operations available through the contract.

CSHARP
interface IPaymentProcessor
{
    bool ProcessPayment(decimal amount);
}

class CardPaymentProcessor : IPaymentProcessor
{
    public bool ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing card payment: {amount}");
        return true;
    }
}

Multiple Interface Implementation

A C# class can implement multiple interfaces. This allows a type to provide several independent capabilities.

CSHARP
interface IPrintable
{
    void Print();
}

interface IExportable
{
    void Export();
}

class Report : IPrintable, IExportable
{
    public void Print()
    {
        Console.WriteLine("Printing report");
    }

    public void Export()
    {
        Console.WriteLine("Exporting report");
    }
}

Interface-Based Polymorphism

An interface reference can point to any object that implements that interface. This allows different implementations to be used through the same contract.

CSHARP
List<INotificationService> services = new()
{
    new EmailNotification(),
    new SmsNotification()
};

foreach (INotificationService service in services)
{
    service.Send("Hello user");
}

interface INotificationService
{
    void Send(string message);
}

class EmailNotification : INotificationService
{
    public void Send(string message)
    {
        Console.WriteLine($"Email: {message}");
    }
}

class SmsNotification : INotificationService
{
    public void Send(string message)
    {
        Console.WriteLine($"SMS: {message}");
    }
}

Explicit Interface Implementation

Explicit implementation allows a class to provide separate implementations when multiple interfaces contain members with the same signature.

CSHARP
interface IPrinter
{
    void Start();
}

interface IScanner
{
    void Start();
}

class OfficeMachine : IPrinter, IScanner
{
    void IPrinter.Start()
    {
        Console.WriteLine("Printer started");
    }

    void IScanner.Start()
    {
        Console.WriteLine("Scanner started");
    }
}

Interface Inheritance

An interface can inherit from another interface, allowing a more specialized contract to build on an existing one.

CSHARP
interface IEntity
{
    int Id { get; }
}

interface IUserEntity : IEntity
{
    string Name { get; }
}

class User : IUserEntity
{
    public int Id { get; }
    public string Name { get; }

    public User(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

Interfaces and Dependency Injection

Interfaces are frequently used with dependency injection. A class can depend on an interface instead of a concrete implementation, making the component easier to replace and test.

CSHARP
interface IMessageSender
{
    void Send(string message);
}

class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"Email sent: {message}");
    }
}

class NotificationManager
{
    private readonly IMessageSender sender;

    public NotificationManager(IMessageSender sender)
    {
        this.sender = sender;
    }

    public void Notify(string message)
    {
        sender.Send(message);
    }
}

Interface vs Abstract Class

FeatureInterfaceAbstract Class
PurposeDefine a contractDefine a base type and shared behavior
Multiple TypesA class can implement multiple interfacesA class can inherit one base class
StateUsually focuses on contract membersCan contain instance state
ImplementationCan define members and, in modern C#, default implementationsCan contain concrete and abstract members
Common UseLoose coupling and capabilitiesShared base behavior

Default Interface Members

Modern C# allows interfaces to provide default implementations for certain members. This can help evolve interfaces while preserving existing implementations in appropriate designs.

CSHARP
interface ILogger
{
    void Log(string message);

    void LogWarning(string message)
    {
        Log($"Warning: {message}");
    }
}

class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

Common Interface Mistakes

  • Creating interfaces for every class without a clear purpose
  • Making interfaces unnecessarily large
  • Adding unrelated responsibilities to one interface
  • Depending directly on concrete implementations
  • Using interfaces when a simple concrete type is sufficient

Interface Design Best Practices

  • Keep interfaces focused on a clear responsibility
  • Prefer small contracts over large interfaces
  • Use meaningful interface names
  • Depend on abstractions when flexibility is required
  • Design interfaces around behavior rather than implementation details
  • Use dependency injection for replaceable services

Real-World Applications

  • Payment processing systems
  • Logging services
  • Notification providers
  • Repository patterns
  • Database abstractions
  • Authentication providers
  • Cloud service integrations

Practice Exercises

  • Create an IShape interface with an Area method
  • Implement the interface using Circle and Rectangle
  • Create an ILogger interface
  • Build two different logging implementations
  • Implement multiple interfaces in one class
  • Create a service using dependency injection
  • Compare an interface design with an abstract class

Conclusion

Interfaces help C# developers design flexible systems by separating contracts from implementations. They are especially useful for polymorphism, dependency injection, testing, and building components that can evolve without tightly coupling the entire application.

Note: Note: Keep interfaces focused and meaningful. Use them when abstraction, replaceable implementations, or loose coupling provides a real benefit.