C# interface
last modified October 16, 2026
In this article we work with interfaces in C#.
An interface defines a contract that must be implemented by classes and structs that implement the interface.
A remote control is an interface between the viewer and the TV. Users of a remote control do not have to understand how a particular remote control actually works; they just need to know the interface: what a red or green button does, or what the arrow buttons do.
Interfaces have two roles:
- APIs
- Contracts
Objects interact with the outside world with the members they expose. The
actual implementation is not important to the programmer, or it might be
secret. A programmer might call a Maximize method on a window of a
GUI toolkit but knows nothing about how this method is implemented. From this
point of view, interfaces are ways through which objects interact with the
outside world, without exposing too much about their inner workings.
From the second point of view, interfaces are contracts. If agreed upon, they must be followed. They are used to design the architecture of an application and they help organize the code. Code that depends on interfaces rather than on concrete classes is easier to test and to change.
Interfaces are declared with the interface keyword. By convention,
their names start with the capital letter I, such as IComparable
or IDisposable. An interface can declare methods, properties,
events, and indexers. Interface members are public by default. A class or a
struct that implements an interface must implement all its members that do not
have a default implementation. An interface cannot contain instance fields.
A C# class can inherit from only one class, but it can implement any number of interfaces. An interface can also extend any number of other interfaces.
There is an important distinction between interfaces and abstract classes.
Abstract classes provide a partial implementation and state for classes that are
related in an inheritance hierarchy. Interfaces, on the other hand, can be
implemented by classes that are not related to each other. For instance, the
Database and SignIn classes are not related, but both
can implement an ILoggable interface.
C# simple interface
The following program uses a simple interface.
var some = new Some();
some.DoInform();
interface IInfo
{
void DoInform();
}
class Some : IInfo
{
public void DoInform()
{
Console.WriteLine("This is Some class");
}
}
This is a simple C# program demonstrating an interface.
interface IInfo
{
void DoInform();
}
This is the IInfo interface. It declares the DoInform
method. The method has no body; it is implicitly public and abstract.
class Some : IInfo
To implement an interface, we put its name after a colon.
public void DoInform()
{
Console.WriteLine("This is Some class");
}
The class provides an implementation for the DoInform method. The
implementing method must be public.
$ dotnet run This is Some class
C# interface polymorphism
Variables, parameters, and collections can have an interface type. They can then hold objects of any class that implements the interface.
List<IShape> shapes = [new Circle(1.5), new Rectangle(2, 3), new Circle(0.5)];
foreach (var shape in shapes)
{
Console.WriteLine($"{shape.Name}: {shape.Area():F2}");
}
Console.WriteLine($"Total: {shapes.Sum(s => s.Area()):F2}");
interface IShape
{
string Name { get; }
double Area();
}
record Circle(double Radius) : IShape
{
public string Name => "circle";
public double Area() => Math.PI * Radius * Radius;
}
record Rectangle(double Width, double Height) : IShape
{
public string Name => "rectangle";
public double Area() => Width * Height;
}
The IShape interface declares a property and a method. Two records
implement it; the code that computes the total area works with the interface
only.
$ dotnet run circle: 7.07 rectangle: 6.00 circle: 0.79 Total: 13.85
C# multiple interfaces
The next example shows how a class can implement multiple interfaces.
var cellPhone = new CellPhone();
cellPhone.SwitchOn();
cellPhone.VolumeUp();
cellPhone.PlugIn();
cellPhone.SwitchOff();
interface IDevice
{
void SwitchOn();
void SwitchOff();
}
interface IVolume
{
void VolumeUp();
void VolumeDown();
}
interface IPluggable
{
void PlugIn();
void PlugOff();
}
class CellPhone : IDevice, IVolume, IPluggable
{
public void SwitchOn() => Console.WriteLine("Switching on");
public void SwitchOff() => Console.WriteLine("Switching off");
public void VolumeUp() => Console.WriteLine("Volume up");
public void VolumeDown() => Console.WriteLine("Volume down");
public void PlugIn() => Console.WriteLine("Plugging in");
public void PlugOff() => Console.WriteLine("Plugging off");
}
We have a CellPhone class that implements three interfaces.
class CellPhone : IDevice, IVolume, IPluggable
The interfaces are separated by commas. The CellPhone class must
implement all members of all three interfaces.
$ dotnet run Switching on Volume up Plugging in Switching off
C# interface inheritance
The next example shows how an interface can inherit from multiple other interfaces.
var db = new DBConnect();
db.DoInform();
db.GetVersion();
db.DoLog();
db.Connect();
ILog log = db;
log.DoInform();
interface IInfo
{
void DoInform();
}
interface IVersion
{
void GetVersion();
}
interface ILog : IInfo, IVersion
{
void DoLog();
}
class DBConnect : ILog
{
public void DoInform() => Console.WriteLine("This is DBConnect class");
public void GetVersion() => Console.WriteLine("Version 1.02");
public void DoLog() => Console.WriteLine("Logging");
public void Connect() => Console.WriteLine("Connecting to the database");
}
We define three interfaces. We can organize interfaces in a hierarchy.
interface ILog : IInfo, IVersion
The ILog interface inherits from two other interfaces. A class
that implements ILog must implement the members of all three
interfaces.
ILog log = db; log.DoInform();
Through the ILog type, we can call the inherited
DoInform method, but not the Connect method, which is
not part of any interface.
$ dotnet run This is DBConnect class Version 1.02 Logging Connecting to the database This is DBConnect class
C# default interface methods
Since C# 8, an interface member can have a default implementation. Classes that implement the interface can use it or provide their own. Default methods allow us to add new members to an interface without breaking the existing implementations.
ILogger[] loggers = [new ConsoleLogger(), new PrefixLogger()];
foreach (var logger in loggers)
{
logger.Log("server started");
logger.LogError("disk is full");
}
interface ILogger
{
void Log(string message);
void LogError(string message) => Log($"ERROR: {message}");
}
class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
}
class PrefixLogger : ILogger
{
public void Log(string message) => Console.WriteLine($"[app] {message}");
public void LogError(string message) => Console.WriteLine($"[app] !!! {message}");
}
The LogError method has a default implementation that calls the
Log method.
void LogError(string message) => Log($"ERROR: {message}");
The ConsoleLogger class uses the default implementation, while the
PrefixLogger class provides its own.
ILogger[] loggers = [new ConsoleLogger(), new PrefixLogger()];
A default method can be called only through the interface type. The call
new ConsoleLogger().LogError("x") would not compile, because the
class itself does not have the method.
$ dotnet run server started ERROR: disk is full [app] server started [app] !!! disk is full
C# explicit interface implementation
If a class implements two interfaces with a member of the same signature, or if we want to hide an interface member from the public API of the class, we can implement the member explicitly. An explicitly implemented member is prefixed with the interface name and has no access modifier.
var doc = new Document();
((IPrintable) doc).Output();
((IExportable) doc).Output();
IExportable exp = doc;
exp.Output();
interface IPrintable
{
void Output();
}
interface IExportable
{
void Output();
}
class Document : IPrintable, IExportable
{
void IPrintable.Output() => Console.WriteLine("Printing document");
void IExportable.Output() => Console.WriteLine("Exporting document to PDF");
}
Both interfaces declare an Output method. The
Document class provides a different implementation for each of
them.
void IPrintable.Output() => Console.WriteLine("Printing document");
An explicitly implemented method can be called only through a reference of the
interface type; doc.Output() would not compile.
$ dotnet run Printing document Exporting document to PDF Exporting document to PDF
C# static abstract interface members
Since C# 11, interfaces can declare static abstract members. They are used in generic code: a type parameter constrained by the interface gives access to the static members of the type argument. This feature is the basis of generic math in .NET.
Console.WriteLine(Describe<Celsius>());
Console.WriteLine(Describe<Fahrenheit>());
static string Describe<T>() where T : ITemperatureScale =>
$"{T.Name}: water freezes at {T.FreezingPoint} and boils at {T.BoilingPoint}";
interface ITemperatureScale
{
static abstract string Name { get; }
static abstract double FreezingPoint { get; }
static abstract double BoilingPoint { get; }
}
struct Celsius : ITemperatureScale
{
public static string Name => "Celsius";
public static double FreezingPoint => 0;
public static double BoilingPoint => 100;
}
struct Fahrenheit : ITemperatureScale
{
public static string Name => "Fahrenheit";
public static double FreezingPoint => 32;
public static double BoilingPoint => 212;
}
The ITemperatureScale interface declares three static properties.
Each implementing struct provides its own values.
static string Describe<T>() where T : ITemperatureScale =>
$"{T.Name}: water freezes at {T.FreezingPoint} and boils at {T.BoilingPoint}";
Inside the generic method, we access the static members through the type
parameter T. No instance of the type is needed.
$ dotnet run Celsius: water freezes at 0 and boils at 100 Fahrenheit: water freezes at 32 and boils at 212
C# built-in interfaces
.NET defines many interfaces that our types can implement to work with the
framework. For instance, IComparable<T> lets collections sort
our objects, and IDisposable lets them be used with the
using statement.
List<Employee> employees =
[
new("Robert", 4500),
new("Lucy", 6200),
new("Thomas", 3800)
];
employees.Sort();
employees.ForEach(Console.WriteLine);
using (var res = new Resource("db connection"))
{
Console.WriteLine("Working with the resource");
}
record Employee(string Name, int Salary) : IComparable<Employee>
{
public int CompareTo(Employee? other) => Salary.CompareTo(other?.Salary ?? 0);
}
class Resource(string name) : IDisposable
{
public void Dispose() => Console.WriteLine($"Releasing {name}");
}
The Sort method of the list uses our CompareTo
method; the employees are sorted by salary. The Dispose method of
the Resource class is called automatically at the end of the
using block.
$ dotnet run
Employee { Name = Thomas, Salary = 3800 }
Employee { Name = Robert, Salary = 4500 }
Employee { Name = Lucy, Salary = 6200 }
Working with the resource
Releasing db connection
Source
Interfaces - define behavior for multiple types
In this article we have covered C# interfaces.
Author
List all C# tutorials.