Implementing Several Interfaces
A class can inherit one base class but implement any number of interfaces, and explicit implementation settles clashing member names.
class Report : BaseClass, IFirst, ISecond { string ISecond.Member() => "..."; }A class in C# can inherit from only one base class. That limit is deliberate, and interfaces are the way around it. A class may implement as many interfaces as you like: list them after the colon, separated by commas. If the class also has a base class, the base class must be written first.
Each interface is a separate promise. The class must supply every member of every interface it names. One method can satisfy two interfaces at once if they declare the same member.
When two interfaces clash
Sometimes two interfaces declare a member with the same name and you need different behaviour for each. Explicit implementation solves this. You write the member with the interface name in front of it, as string IExportable.Render(). That member is reachable only through a variable of that interface type, never through the class itself.
Explicit implementation has a second use. It keeps a member off the class's public surface when callers have no business calling it directly.
What trips people up
An explicit implementation takes no access modifier. Writing public string IExportable.Render() is a compile error, because the member's visibility is decided by the interface, not by you. And because the member is invisible on the class, report.Render() will not compile unless an ordinary implementation also exists. Cast to the interface first, or declare the variable as the interface type.
Example
var report = new SalesReport();
// Through the class: the ordinary implementation runs.
Console.WriteLine(report.Render());
// Through each interface: each one gets its own answer.
IPrintable printable = report;
IExportable exportable = report;
Console.WriteLine(printable.Render());
Console.WriteLine(exportable.Render());
interface IPrintable
{
string Render();
}
interface IExportable
{
string Render();
}
class SalesReport : IPrintable, IExportable
{
// Ordinary implementation. It satisfies IPrintable and is callable on the class.
public string Render() => "plain text";
// Explicit implementation. Reachable only through an IExportable variable.
// No access modifier is allowed on this line.
string IExportable.Render() => "csv";
}
// Output:
// plain text
// plain text
// csvWhen to use it
- Give a type that already extends a framework base class an extra capability your own code needs.
- Expose a clean public surface while still satisfying an interface a library insists on.
- Let one class be both printable and archivable so two unrelated subsystems can each accept it.
More examples
Three small promises on one class
Several narrow interfaces describe separate capabilities, and one class takes on all three. Prefer this to one wide interface: a caller that only needs the name can ask for INamed and will then accept types that have nothing else in common.
interface INamed
{
string Name { get; }
}
interface IArchivable
{
string ArchiveKey();
}
interface IValidatable
{
bool IsValid();
}
class Upload : INamed, IArchivable, IValidatable
{
public Upload(string name, int sizeInBytes)
{
Name = name;
SizeInBytes = sizeInBytes;
}
public string Name { get; }
public int SizeInBytes { get; }
public string ArchiveKey() => $"uploads/{Name}";
public bool IsValid() => SizeInBytes > 0;
}
var upload = new Upload("notes.txt", 12);
Console.WriteLine(upload.IsValid());
Console.WriteLine(upload.ArchiveKey());
// Output:
// True
// uploads/notes.txtExplicit implementation for two unit systems
Same member name, two different meanings. Making both implementations explicit forces every caller to say which unit it wants, which is exactly the clarity you want when a silent choice of unit would be a bug.
interface IMetric
{
double GetLength(); // metres
}
interface IImperial
{
double GetLength(); // feet
}
class Runway : IMetric, IImperial
{
private readonly double _metres;
public Runway(double metres) => _metres = metres;
// Both are explicit, so Runway itself exposes no GetLength at all.
double IMetric.GetLength() => _metres;
double IImperial.GetLength() => _metres * 3.28084;
}
var runway = new Runway(1000);
Console.WriteLine(((IMetric)runway).GetLength());
Console.WriteLine(((IImperial)runway).GetLength());
// Output:
// 1000
// 3280.84A base class and interfaces together
One base class for what the type is, interfaces for what it can do. Writing the interfaces before the base class is a compile error, which is the most common build break people hit the first time they combine the two.
abstract class Document
{
protected Document(string title) => Title = title;
public string Title { get; }
}
interface IPrintable
{
string Render();
}
interface IArchivable
{
string ArchiveKey();
}
// The base class must come first in this list. Interfaces follow in any order.
class Memo : Document, IPrintable, IArchivable
{
public Memo(string title) : base(title)
{
}
public string Render() => $"memo: {Title}";
public string ArchiveKey() => $"memos/{Title}";
}
Console.WriteLine(new Memo("budget").Render());
// Output:
// memo: budget
Discussion