C# Interface Implementation
In this tutorial, we'll dive into how to implement interfaces in C#. Understanding interface implementation is crucial for writing clean, maintainable, and flexible code in object-oriented programming.
Introduction to Interface Implementation
An interface in C# defines a contract that classes must follow, but it doesn't provide any implementation details itself. When a class implements an interface, it must provide concrete implementations for all members defined in that interface. This creates a guaranteed set of functionalities that any implementing class will provide.
Basic Interface Implementation
Let's start with a simple example of how to implement an interface in C#:
// Define an interface
public interface ILogger
{
void LogMessage(string message);
void LogError(string error);
}
// Implement the interface in a class
public class FileLogger : ILogger
{
public void LogMessage(string message)
{
Console.WriteLine($"Log to file: {message}");
}
public void LogError(string error)
{
Console.WriteLine($"Error log to file: {error}");
}
}
In this example, FileLogger
implements the ILogger
interface. It must provide implementations for both LogMessage
and LogError
methods defined in the interface.
Implementing Multiple Interfaces
A class in C# can implement multiple interfaces simultaneously:
public interface IDataReader
{
string ReadData();
}
public interface IDataWriter
{
void WriteData(string data);
}
// Implementing multiple interfaces
public class FileHandler : IDataReader, IDataWriter
{
public string ReadData()
{
return "Data read from file";
}
public void WriteData(string data)
{
Console.WriteLine($"Writing to file: {data}");
}
}
The FileHandler
class now must implement all methods from both IDataReader
and IDataWriter
interfaces.
Explicit Interface Implementation
Sometimes you might need to implement the same method name from different interfaces or want to hide interface implementations from the class's public API. C# allows explicit interface implementation for such scenarios:
public interface IDrawable
{
void Draw();
}
public interface IPrintable
{
void Draw(); // Same method name as in IDrawable
void Print();
}
// Using explicit implementation
public class Document : IDrawable, IPrintable
{
// Explicit implementation for IDrawable
void IDrawable.Draw()
{
Console.WriteLine("Drawing for display");
}
// Explicit implementation for IPrintable
void IPrintable.Draw()
{
Console.WriteLine("Drawing for printing");
}
public void Print()
{
Console.WriteLine("Printing document");
}
}
Usage example:
Document doc = new Document();
// doc.Draw(); // This won't compile because Draw is implemented explicitly
// To call the Draw methods:
((IDrawable)doc).Draw(); // Outputs: Drawing for display
((IPrintable)doc).Draw(); // Outputs: Drawing for printing
// Print is implemented implicitly, so it can be called directly
doc.Print(); // Outputs: Printing document
Interface Implementation with Properties
Interfaces can also define properties that implementing classes must provide:
public interface IVehicle
{
int Speed { get; set; }
string Model { get; }
void Start();
}
public class Car : IVehicle
{
public int Speed { get; set; } // Implemented property with getter and setter
public string Model { get; } // Implemented read-only property
public Car(string model)
{
Model = model;
}
public void Start()
{
Console.WriteLine($"The {Model} is starting. Current speed: {Speed}");
}
}
Example usage:
Car myCar = new Car("Tesla Model 3");
myCar.Speed = 60;
myCar.Start(); // Outputs: The Tesla Model 3 is starting. Current speed: 60
// Using the interface reference
IVehicle vehicle = myCar;
Console.WriteLine($"Vehicle model: {vehicle.Model}"); // Outputs: Vehicle model: Tesla Model 3
Default Interface Methods (C# 8.0+)
As of C# 8.0, interfaces can include default implementations for methods:
public interface INotification
{
void SendNotification(string message);
// Default implementation
void SendUrgentNotification(string message)
{
Console.WriteLine($"URGENT: {message}");
SendNotification(message);
}
}
public class EmailNotifier : INotification
{
public void SendNotification(string message)
{
Console.WriteLine($"Sending email: {message}");
}
// No need to implement SendUrgentNotification as it has a default implementation
}
Usage:
EmailNotifier emailService = new EmailNotifier();
emailService.SendNotification("Hello"); // Outputs: Sending email: Hello
// Using the default implementation
((INotification)emailService).SendUrgentNotification("System failure");
// Outputs:
// URGENT: System failure
// Sending email: System failure
Real-World Example: Dependency Injection
One practical application of interfaces is in dependency injection, which is widely used in modern software development for creating loosely coupled systems:
public interface IUserRepository
{
User GetById(int id);
List<User> GetAllUsers();
void Add(User user);
}
public class SqlUserRepository : IUserRepository
{
// SQL Server implementation
public User GetById(int id)
{
Console.WriteLine($"Getting user {id} from SQL database");
return new User { Id = id, Name = "John" };
}
public List<User> GetAllUsers()
{
Console.WriteLine("Getting all users from SQL database");
return new List<User>() { new User { Id = 1, Name = "John" } };
}
public void Add(User user)
{
Console.WriteLine($"Adding user {user.Name} to SQL database");
}
}
public class UserService
{
private readonly IUserRepository _userRepository;
// Constructor injection of the interface
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int id)
{
return _userRepository.GetById(id);
}
public void RegisterUser(string name)
{
var user = new User { Name = name };
_userRepository.Add(user);
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
Usage example:
// In a real application, this would typically be done by a DI container
IUserRepository repository = new SqlUserRepository();
UserService service = new UserService(repository);
// Use the service
User user = service.GetUser(1);
Console.WriteLine($"Retrieved user: {user.Name}");
service.RegisterUser("Alice");
This pattern allows you to easily swap out implementations (e.g., replace SqlUserRepository
with MongoUserRepository
) without changing the UserService
class.
Interface Implementation Best Practices
-
Keep interfaces small and focused: Follow the Interface Segregation Principle (the "I" in SOLID) - many specific interfaces are better than one general-purpose interface.
-
Name interfaces with the prefix 'I': This is a common convention in C# to clearly identify interfaces.
-
Use interfaces for abstraction: Define interfaces for external dependencies and services to make testing and extending your code easier.
-
Be consistent with implementation: Implement all members of an interface properly and ensure they work as expected.
-
Consider explicit implementation when the interface method could conflict with other methods or should be hidden from the class's normal API.
Summary
Interface implementation is a powerful feature in C# that enables:
- Creating contracts that classes must follow
- Achieving polymorphism without inheritance
- Building flexible, modular, and testable systems
- Developing based on abstractions rather than concrete implementations
By understanding how to implement interfaces correctly, you'll be able to build more maintainable and flexible C# applications.
Exercises
-
Create an interface
IShape
with methodsCalculateArea()
andCalculatePerimeter()
. Implement this interface for at least three different shapes. -
Design a
IPaymentProcessor
interface and implement it for different payment methods (e.g., credit card, PayPal, crypto). -
Create a simple logging system with an
ILogger
interface and multiple implementations (console logger, file logger, database logger). -
Implement an interface that defines CRUD operations and create two different implementations for it (e.g., for SQL and for a web API).
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)