Menu

C# Interface: Declare, Implement, Default Methods and Examples

How interfaces work in C#: declaring one, implementing it in classes and structs, using the interface as a type, implementing several at once, explicit implementation, the framework interfaces you will implement most (IComparable<T>, IEnumerable<T>, IDisposable), and C# 8 default interface methods.

This page includes runnable editors - edit, run, and see output instantly.

An interface is a contract: a named list of members that a type promises to provide. It says what a type can do and nothing about how. Classes and structs implement interfaces, and code written against the interface works with every implementation, including ones written later.

Declaring and implementing an interface

An interface declares members without bodies. By convention its name starts with I. A class implements it by listing it after a colon and providing a public member for each one:

Output:

15% off: 68
10 off: 70
Best for 80: 68
Best for 40: 30

BestPrice knows nothing about PercentOff or FixedOff. It depends only on IDiscount, so adding a BuyOneGetOne class later needs no change to it. That decoupling is the whole reason interfaces exist.

A few rules about the declaration:

  • Interface members are public by default, and the implementing members must be public too (unless implemented explicitly, below).
  • An interface can declare methods, properties, indexers and events. It cannot declare instance fields or constructors.
  • A class that leaves out a member does not compile: error CS0535, "'FixedOff' does not implement interface member 'IDiscount.Apply(decimal)'".
  • You cannot create an interface with new. You create an implementing class and can hold it in a variable of the interface type: IDiscount d = new FixedOff(5);.

The interface as a type

A variable, parameter, field or return type can be declared as an interface. It then accepts any object that implements it, and exposes only the interface's members. This is how you write code that does not care which implementation it receives, and how tests swap in a fake:

public class Checkout
{
    private readonly IPaymentGateway gateway;          // not StripeGateway, not PayPalGateway
    public Checkout(IPaymentGateway gateway) { this.gateway = gateway; }

    public bool Pay(decimal amount) => gateway.Charge(amount);
}

// Production: new Checkout(new StripeGateway(apiKey))
// Unit test:  new Checkout(new FakeGateway(alwaysSucceeds: true))

Dependency injection containers in ASP.NET Core are built on this: services are registered and requested by interface.

Implementing several interfaces

A class has at most one base class but can implement any number of interfaces. Base class first, then the interfaces, separated by commas:

Output:

INVOICE Studio rent March: 950.00
True
True

Interfaces can also inherit from other interfaces: interface IRepository<T> : IReadRepository<T> adds members to the ones it inherits, and a class implementing IRepository<T> must provide both sets.

Explicit interface implementation

Sometimes two interfaces declare a member with the same name and different meanings, or an interface member makes no sense on the class's own public surface. Implement it explicitly by prefixing the interface name and leaving out the access modifier:

Output:

Desk lamp,34.90
Desk lamp for 34.90

An explicitly implemented member is reachable only through the interface. The framework uses this to keep noise off common types: arrays implement IList.Add explicitly (it throws NotSupportedException, since arrays have a fixed size), so the method does not show up when you type array..

Interfaces from the framework you will implement

Implementing a standard interface plugs your type into existing framework code. Three appear constantly.

IComparable<T> gives a type a natural order, which List<T>.Sort(), Array.Sort and Max() use. CompareTo returns a negative number, zero or a positive number:

Output:

1.4, 2.9, 2.10, 10.0

Sorting the same versions as strings would give 1.4, 10.0, 2.10, 2.9. Without IComparable<T>, Sort() throws InvalidOperationException because it has no way to compare two Version objects.

IEnumerable<T> makes a type usable in foreach and with LINQ. The easy way to implement GetEnumerator is with yield return, covered on the IEnumerable and yield page.

IDisposable marks a type that holds something that must be released (a file handle, a connection, a timer). Its one method, Dispose, is what the using statement calls when the block ends, even if an exception is thrown:

Output:

open sales.txt
  write to sales.txt: March total: 12400
close sales.txt
after using

Default interface methods (C# 8)

Adding a member to a published interface used to break every class that implemented it. Since C# 8, an interface member can have a body, which implementing classes inherit unless they provide their own:

public interface ILogger
{
    void Write(string message);

    // New in version 2 of the library. Existing implementers keep compiling.
    void Error(string message) => Write("ERROR: " + message);
}

public class ConsoleLogger : ILogger
{
    public void Write(string message) => Console.WriteLine(message);
}

ILogger log = new ConsoleLogger();
log.Error("disk full");            // ERROR: disk full

var direct = new ConsoleLogger();
// direct.Error("x");              // does not compile: the default method belongs to the interface

The last line is the part that surprises people: a default method is not inherited into the class's own members, so it is callable only through the interface type. Default methods also cannot use instance fields (interfaces have none); they work through the other interface members. They require .NET Core 3.0 or later and are not available on .NET Framework.

C# 8 also allowed static members in interfaces, and C# 11 added static abstract members, which let generic code call a static method or operator on T. That is what generic math in .NET 7 is built on (INumber<T>, where T : INumber<T>).

Interface or abstract class?

Use an interface for a capability that unrelated types can share, when structs should take part, or when a class needs several such roles. Use an abstract class when the implementations are variations of one thing and share state or a fixed algorithm. The abstract classes page has a side-by-side table. Many designs use both: an interface for consumers, and an abstract base class that implements the boring parts for implementers.

Common mistakes

  • Missing or non-public implementation. Every interface member needs a public member with a matching signature, or an explicit implementation. Otherwise CS0535 (or CS0737 when the method exists but is not public).
  • Interfaces with one implementation "just in case". An interface earns its place when there are several implementations or a test fake. Otherwise it is one more file to navigate.
  • Fat interfaces. An interface with twenty members forces every implementer to write twenty members. Split it into smaller roles (IReader, IWriter) that classes combine.
  • Casting an interface back to a class. ((StripeGateway)gateway).Refund() undoes the decoupling. If callers need Refund, it belongs in the interface.
  • Expecting to call a default interface method on the class. It is reached through the interface type.

Frequently Asked Questions

What is an interface in C#?

An interface is a named set of members (methods, properties, events, indexers) that a type promises to provide, with no instance state. interface IDiscount { decimal Apply(decimal price); } says "anything that is an IDiscount can apply itself to a price". Classes and structs implement it with a colon, and code can then work with any of them through the interface type.

How do I implement an interface in C#?

List it after a colon in the class declaration (after the base class, if there is one) and provide a public member for each interface member: class HolidayDiscount : IDiscount { public decimal Apply(decimal price) => price * 0.9m; }. A missing member is error CS0535. Visual Studio and Rider can generate the stubs with a quick fix.

Can a C# class implement multiple interfaces?

Yes, as many as needed, separated by commas: class Invoice : IPrintable, IComparable<Invoice>, IDisposable. This is C#'s answer to multiple inheritance: a class has one base class but can play many roles. If two interfaces declare the same member, one public method can satisfy both, or you can use explicit implementation to give each its own.

What is explicit interface implementation in C#?

Implementing a member with the interface name in front and no access modifier: string IExportable.Format() { ... }. The member is then callable only through a variable of the interface type, not through the class. It is used to resolve name clashes between two interfaces and to keep rarely used interface members off the class's public surface.

What are default interface methods in C#?

Since C# 8, an interface member can have a body: void Error(string m) => Write("ERROR: " + m);. Implementing classes get it for free and may provide their own. The feature exists so library authors can add members to a published interface without breaking every implementer. A default method is reached through the interface type, not through the class.

Why do C# interface names start with I?

It is the .NET naming convention (IEnumerable, IDisposable, IComparable<T>): a capital I followed by a PascalCase name, often an adjective ending in "-able". The compiler does not require it, but following it makes interface types recognizable at a glance, especially in a class declaration where the base class and interfaces share one list.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED