AlgoMaster Logo

Multiple Interface Inheritance

Last Updated: May 17, 2026

10 min read

C# lets a class inherit from at most one base class, but it lets that same class implement as many interfaces as you want. This chapter covers how to declare a class that implements several interfaces, how interfaces themselves can extend other interfaces, how to cast between the capabilities a class exposes, and how to resolve the diamond problem that appears when two parent interfaces ship default methods with the same signature.

Single Base Class, Many Interfaces

The rule is short: one base class, unlimited interfaces. The syntax puts the base class first, then a comma-separated list of interfaces. The compiler infers from context which name is the class and which names are interfaces, but convention writes the base class first to make the code easy to read.

Order inherits state and behavior from Entity (the Id and CreatedAt) and picks up three capability contracts from IDiscountable, IShippable, and IPayable. The class is one class. The capabilities are independent. Each interface contributes its own members, and the class implements each contract without any of them interfering with the others.

The order in the declaration list matters only for the base class. The base class, if you have one, must be the first name after the colon. Everything after that is interfaces, and their order is purely a style choice.

The compiler error you'll see if you try to list two classes after the colon is CS1721: Class 'Order' cannot have multiple base classes: 'Entity' and 'AuditLog'. That's the language reminding you that the single-inheritance rule applies to classes only, not to interfaces.

Why C# Chose This Design

C# settled on single class inheritance plus unlimited interface implementation as a deliberate language design choice. The decision rules out a category of problems that show up in languages with multiple class inheritance.

In a language like C++, a class can inherit from two parent classes, and if both parents carry the same field name or method name, the compiler has to invent rules to decide which one wins. Worse, if both parents themselves inherit from a common grandparent, the grandparent's state can end up duplicated in the final object, leading to the well-known "diamond of death" where one logical field exists twice in memory and nobody is sure which copy to update.

C# sidesteps the problem by saying that state and implementation can only flow through one inheritance path. A class has exactly one parent class, so there's never ambiguity about where its fields and non-virtual methods come from. Interfaces, on the other hand, carry no state of their own. They describe a contract, not a memory layout. Implementing fifty interfaces adds no fields and no constructors to your class. It just adds promises that the class fulfils.

That separation gives you most of the flexibility of multiple inheritance without the cost. You can compose a class out of any number of capability contracts (IDiscountable, IShippable, IComparable<T>, IEnumerable<T>, and so on) without worrying about which parent's fields you've inherited or which constructor runs first.

The Mermaid diagram below shows how Order sits at the bottom of one inheritance chain and at the bottom of multiple capability contracts at the same time:

The solid arrow from Entity to Order is class inheritance: state flows down. The dashed arrows from each interface are implementations: contracts flow down, but no state and no method bodies travel with them (unless the interface ships a default method, which we'll come back to). The cyan node is the single base class. The orange nodes are the capability interfaces. The teal node is the concrete class that pulls them all together.

Interfaces Extending Interfaces

An interface can itself inherit from another interface, or from several. The syntax is identical to a class implementing interfaces, except that an interface extends interfaces only, never a class. When an interface extends another, every member of the parent interface becomes part of the derived interface's contract.

Order declares it implements IRefundable. There's no need to also write IPayable in the declaration list, because IRefundable already extends IPayable. The class still has to implement Pay, though. Inheritance between interfaces makes the parent's members part of the derived interface, so the implementer is on the hook for every method in the combined contract.

You can list the parent interface explicitly if you want to make the inheritance chain visible at the class declaration. The result is the same:

The compiler treats the two declarations identically. Which one you write is a style choice, although listing the most-derived interface alone is the more common convention because it reads as the strongest statement of intent.

An interface can extend more than one parent interface in the same way a class implements multiple interfaces:

IPromotableShippable is a compound interface. A class that implements it has to provide both ApplyDiscount and CalculateShippingCost. This is a common pattern when a single role in your domain combines multiple smaller capabilities, and you want callers to ask for the role rather than for each capability separately.

Each Interface Contributes Members Independently

When a class implements several interfaces, each interface contributes its own set of members. The class needs to satisfy every contract, and the same method can satisfy more than one contract at once if the signatures line up.

Two interfaces, one method body, both contracts satisfied. The class isn't required to write Describe twice. The compiler maps the same implementation onto both interface slots.

When the two interfaces want genuinely different behavior under the same name, you'd reach for explicit interface implementation. That syntax lets you write two distinct method bodies, each tied to one interface. The takeaway here is that when the implementations should agree, one method is enough. When they should differ, the language gives you a way to provide both.

The cost of implementing more interfaces is small but worth knowing about. Each interface a class implements adds an entry to the type's interface map, which is the table the CLR consults at runtime when it dispatches an interface call. Lookups are constant-time, so the cost shows up only when you create the type, not every time you call a method. Even on a class with twenty interfaces, the per-instance overhead is on the order of bytes and the per-call overhead is unmeasurable.

Casting Across the Interfaces a Class Implements

A class that implements several interfaces can be viewed through any of them. The same object can be referenced as Order, as IPayable, as IShippable, or as IDiscountable. Each reference shows a different slice of the object's API.

is, as, and an explicit cast all check whether the runtime object implements the target interface. is and pattern matching are the modern way to do this when you want to handle the matching and non-matching cases in one expression. Explicit casts are fine when you already know the object implements the interface and you'd rather have an exception if it doesn't. The as operator returns null on a failed cast, which fits well into null-conditional chains.

Filtering a heterogeneous collection by capability is a natural use case. Suppose you have a list of cart items, some of which are shippable and some of which aren't (digital downloads, gift cards, services):

The loop doesn't care what concrete types are in the cart. It only asks whether each item satisfies the IShippable contract, and if it does, adds the shipping cost. New shippable types can be added to the codebase without touching this loop.

The Diamond Problem with Default Interface Methods

C# 8 added default implementations to interfaces, which means an interface can ship a method body, not just a method signature. That feature reopens a question that pure interfaces had avoided: what happens when a class implements two interfaces that both provide a default implementation for the same method?

This is the diamond problem, and the compiler catches it. Here's a setup that triggers it:

Calling d.ApplyDiscount(order.Total) through an IDiscountable reference actually compiles and runs, returning the IDiscountable default of 90. The reason is that through that reference, only IDiscountable.ApplyDiscount is in scope. The same is true for an ILoyaltyDiscount reference, which returns 80. The diamond only becomes a real problem when callers try to call the method through a class reference, where both interfaces are in scope at once:

The compiler emits:

The class inherited two competing implementations from two unrelated interfaces, and the compiler refuses to pick a winner silently. The decision is forced onto the implementer.

The Mermaid diagram below shows the shape of the conflict. Two interfaces, each with a default implementation, both flow into the same class:

The red Order node marks the conflict site. Both orange interfaces try to flow their default implementation down into the same method slot on the class. The compiler can't pick one, so the design needs an explicit choice.

Resolving the Diamond

Two patterns resolve the ambiguity, both of which put the implementer in charge of the decision.

Pattern 1: Override the method on the class.

The class provides its own implementation, which the compiler treats as the most-specific method for the conflicting slot. The two interface defaults are still there, but neither is the one that gets called through the class reference:

Once the class provides its own ApplyDiscount, that one wins from every angle. Calling through the class reference, the IDiscountable reference, or the ILoyaltyDiscount reference all dispatch to the same class method. The two interface defaults still exist in their interfaces, but they're shadowed by the class member.

Pattern 2: Explicit interface implementations.

If you genuinely want different behavior depending on which interface the caller went through, use explicit interface implementation to provide two distinct method bodies. Each one is invocable only through its specific interface reference:

The class still has to be cast to one specific interface to use ApplyDiscount, because neither version is accessible directly off the class. order.ApplyDiscount(...) would fail to compile, because the class has no publicly accessible method by that name. The caller has to commit to one interface or the other, which is exactly the disambiguation the diamond demanded.

Pattern 3: Call a specific interface's default from inside an override.

If you want to override on the class but still reuse one of the interface defaults, you can call the default explicitly using IFoo.Method() syntax. This is the way to say "use this specific parent's implementation":

The cast ((ILoyaltyDiscount)this).ApplyDiscount(originalTotal) reaches into the ILoyaltyDiscount default specifically, ignoring IDiscountable's version. From there, the class layers its own behavior on top. This pattern is useful when you want one of the defaults as a starting point rather than starting from scratch.

A Realistic Example: Capability Interfaces

Pulling the pieces together, here's an Order that implements four capability interfaces, where one of them extends another, and demonstrates casting between them.

Order is one class, but callers can see it as any of the four capability interfaces. Code that only cares about payment talks to IPayable (which it gets through IRefundable). Code that only cares about shipping talks to IShippable. New capabilities can be added by introducing another interface and implementing it on the class, without breaking any existing callers.

The classic name for this style is the interface segregation principle: prefer many small, focused interfaces over a few large, do-everything ones. When each capability is its own interface, callers can ask for exactly what they need and nothing more, and the same concrete class can compose any combination of capabilities without committing to a deep class hierarchy.

Summary

  • A C# class can extend at most one base class, but it can implement any number of interfaces. The base class, if present, comes first in the declaration list; interfaces follow in any order.
  • An interface can itself inherit from one or many parent interfaces. A class that implements a derived interface must implement every member of every interface in the chain.
  • Each implemented interface contributes its members independently. One method body can satisfy two interfaces if their signatures match, or explicit interface implementation can give each its own body.
  • A class instance can be cast to any interface it implements using is, as, or an explicit cast. This is how heterogeneous collections filter by capability.
  • The diamond problem appears when two interfaces ship default implementations of the same method. The compiler emits CS8705 and forces the implementer to pick a winner.
  • Resolve the diamond by overriding the method on the class, by providing explicit interface implementations for each interface, or by calling one parent's default explicitly via IFoo.Method() syntax.
  • Each interface a class implements adds a small entry to the type's interface map. The cost is paid once per type, not per call, and is unmeasurable in practice.
  • C# chose single class inheritance plus unlimited interface implementation to give you the flexibility of multiple inheritance without the ambiguity that comes with inheriting state from multiple parents.

The next chapter compares abstract classes and interfaces head to head: when each is the right tool, what they can express that the other can't, and how the trade-offs play out in real designs.