In C#, an interface is a contract that defines a set of methods, properties, events, or indexers that a class or struct must implement. Interfaces do not contain any implementation, only declarations. They provide a way to enforce certain capabilities in different classes and ensure consistency without dictating how those capabilities should be implemented.
In this tutorial, we’ll explore what interfaces are in C#, how to define and implement them, when and why to use them, and several real-world use cases. Additionally, we’ll highlight key takeaways to summarize the important concepts.
In C#, an interface is a way to define a contract or blueprint for classes or structs. It specifies what methods, properties, or events a class must implement, but it does not provide the actual implementation of these members.
public interface IVehicle
{
void Start();
void Stop();
int Speed { get; set; }
}
This interface defines three members:
Start
and Stop
Speed
Any class that implements this interface will be required to provide an implementation for these members.
Interfaces allow you to decouple code by ensuring that different parts of a program depend on abstractions (interfaces) rather than concrete implementations. This promotes flexibility and easier code maintenance.
Interfaces enable polymorphism, meaning you can write code that works with objects of different classes in the same way as long as those classes implement the same interface. This is useful in scenarios where multiple classes need to share behavior but not inheritance.
Since C# does not support multiple inheritance for classes, interfaces offer a way to achieve similar functionality by allowing a class to implement multiple interfaces.
Interfaces provide a clear contract that guarantees certain functionality will be present, which makes the code more reliable and easier to understand.
An interface is defined using the interface keyword. It can declare methods, properties, events, or indexers, but it does not provide any implementation.
public interface IDriveable
{
void Accelerate();
void Brake();
int MaxSpeed { get; }
}
A class that implements an interface must provide concrete implementations for all the interface members. You use the semi-colon(:
) in the class definition.
public class Car : IDriveable
{
public int MaxSpeed { get; private set; }
public Car(int maxSpeed)
{
MaxSpeed = maxSpeed;
}
public void Accelerate()
{
Console.WriteLine("The car is accelerating.");
}
public void Brake()
{
Console.WriteLine("The car is braking.");
}
}
class Program
{
static void Main()
{
IDriveable myCar = new Car(200);
myCar.Accelerate();
myCar.Brake();
Console.WriteLine($"Max speed: {myCar.MaxSpeed}");
}
}
This code defines a Car
class that implements the IDriveable
interface. The class provides its own specific implementation for the methods and properties defined by the interface.
In C#, a class cannot inherit from more than one base class, but it can implement multiple interfaces. This allows you to create complex, flexible designs without the limitations of single inheritance.
public interface IVehicle
{
void Start();
void Stop();
}
public interface IMachine
{
void Maintain();
}
public class Truck : IVehicle, IMachine
{
public void Start()
{
Console.WriteLine("Truck started.");
}
public void Stop()
{
Console.WriteLine("Truck stopped.");
}
public void Maintain()
{
Console.WriteLine("Truck is being maintained.");
}
}
class Program
{
static void Main()
{
Truck myTruck = new Truck();
myTruck.Start();
myTruck.Maintain();
myTruck.Stop();
}
}
Here, the Truck
class implements two interfaces: IVehicle
and IMachine
. This allows it to behave both as a vehicle and a machine, demonstrating the power of multiple interface implementation.
Interfaces are commonly used in Dependency Injection (DI), a pattern where dependencies are injected into a class rather than being created by the class itself. This is often used to create more testable, modular code.
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
}
public class Application
{
private readonly ILogger _logger;
public Application(ILogger logger)
{
_logger = logger;
}
public void Run()
{
_logger.Log("Application is running.");
}
}
In this example, the Application
class depends on ILogger
, not a specific logging implementation. This makes the code more flexible and easier to maintain.
The IEnumerable<T>
and IEnumerator<T>
interfaces allow a collection to be iterated over. Many built-in collections in C# like List<T>
and Dictionary<TKey, TValue>
implement these interfaces.
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
foreach (string name in names)
{
Console.WriteLine(name);
}
This use of the IEnumerable<T>
interface allows you to use the foreach
loop to iterate over the list.
In unit testing, interfaces are commonly used to mock dependencies. For example, you can create a mock implementation of an interface to simulate behavior for testing purposes.
public interface ICalculator
{
int Add(int a, int b);
}
public class Calculator : ICalculator
{
public int Add(int a, int b)
{
return a + b;
}
}
In testing, you can mock ICalculator
to test how a class that uses it behaves without needing a real implementation.
IShape
or IRepository
.C# interfaces are a cornerstone of object-oriented programming, allowing you to define a contract that multiple classes can adhere to, promoting loose coupling and code reuse. They are especially useful in large, complex applications where modularity, flexibility, and testability are key concerns. Whether you’re implementing design patterns like dependency injection or designing a clean API, interfaces give you the ability to abstract behavior and provide a powerful mechanism to build robust, maintainable, and scalable systems.
Understanding how to properly define, implement, and use interfaces will greatly enhance your ability to write flexible, reusable, and maintainable C# code.