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.
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.
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.
interface IRepository
{
}
interface IPaymentService
{
}
interface INotificationService
{
}
Interfaces with Properties
Interfaces can define properties that implementing classes must provide.
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.
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.
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.
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.
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.
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.
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
| Feature | Interface | Abstract Class |
|---|---|---|
| Purpose | Define a contract | Define a base type and shared behavior |
| Multiple Types | A class can implement multiple interfaces | A class can inherit one base class |
| State | Usually focuses on contract members | Can contain instance state |
| Implementation | Can define members and, in modern C#, default implementations | Can contain concrete and abstract members |
| Common Use | Loose coupling and capabilities | Shared 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.
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.