Interfaces in C#
Defining contracts with interfaces, and why they're how C# achieves multiple-inheritance-like flexibility.
2 min read
An interface defines a contract — a set of members a class promises to implement — without providing any implementation itself. It answers "what can this thing do?" without caring what it is.
Defining and implementing an interface
interface IShape
{
double GetArea();
}
class Circle : IShape
{
public double Radius;
public Circle(double radius) => Radius = radius;
public double GetArea() => Math.PI * Radius * Radius;
}
class Rectangle : IShape
{
public double Width, Height;
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public double GetArea() => Width * Height;
}By convention, interface names start with I — IShape, IDisposable, IEnumerable — which makes them instantly recognizable when you're scanning code.
Why interfaces matter: programming to an abstraction
The payoff is writing code that only cares about the interface, not the concrete type:
List<IShape> shapes = new List<IShape> { new Circle(5), new Rectangle(4, 6) };
foreach (var shape in shapes)
{
Console.WriteLine(shape.GetArea());
}This function doesn't know or care whether it's holding a Circle or a Rectangle — only that whatever it holds can GetArea(). Add a Triangle class implementing IShape next year, and this loop needs zero changes.
A class can implement multiple interfaces
Unlike class inheritance (limited to one base class), a class can implement as many interfaces as it needs:
interface IPrintable
{
void Print();
}
class Invoice : IShape, IPrintable
{
public double GetArea() => 0; // not really a shape, but hypothetically
public void Print() => Console.WriteLine("Printing invoice...");
}This is how C# gives you the flexibility of "inheriting" multiple behaviors without the ambiguity problems of multiple class inheritance — interfaces carry no state and no conflicting implementation, so there's nothing to collide.
Default interface methods
Since C# 8, interfaces can provide a default implementation, which implementing classes may override or simply inherit as-is:
interface IGreeter
{
void Greet(string name) => Console.WriteLine($"Hello, {name}");
}
class FormalGreeter : IGreeter
{
public void Greet(string name) => Console.WriteLine($"Good day, {name}.");
}
class CasualGreeter : IGreeter { } // uses the interface's default GreetThis is mainly useful for evolving an interface without breaking every existing implementer — you can add a new member with a sensible default rather than forcing every class that implements the interface to be updated immediately.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.