Explicit Interfaces
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 28 of 70.
When a class implements multiple interfaces that have members with the same name, you face a naming collision. Explicit interface implementation solves this by letting you provide separate implementations for each interface.
With explicit implementation, you prefix the member name with the interface name and omit the access modifier:
public interface IAmericanPlug
{
void Connect();
}
public interface IEuropeanPlug
{
void Connect();
}
public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
void IAmericanPlug.Connect()
{
Console.WriteLine("Connected to 120V");
}
void IEuropeanPlug.Connect()
{
Console.WriteLine("Connected to 220V");
}
}Explicitly implemented members can only be accessed through a variable of the interface type, not through the class itself:
var adapter = new UniversalAdapter();
// adapter.Connect(); // Won't compile!
IAmericanPlug american = adapter;
american.Connect(); // Connected to 120V
IEuropeanPlug european = adapter;
european.Connect(); // Connected to 220VThis technique is also useful when you want to hide interface members from the class's public API. If a method only makes sense when treating the object as a specific interface, explicit implementation keeps your class's surface area clean while still fulfilling the contract.
Challenge
EasyLet's build a report generator system that demonstrates how explicit interface implementation lets a single class provide different behaviors depending on which interface is being used. You'll create a document that can be formatted differently for display versus printing.
You'll organize your code across four files:
IDisplayable.cs: Define an interface calledIDisplayablein theReportsnamespace. This interface represents content that can be shown on screen. It should declare a methodRender()that returns a string.IPrintable.cs: Define an interface calledIPrintablein theReportsnamespace. This interface represents content that can be sent to a printer. It should also declare a methodRender()that returns a string - notice this creates a naming collision withIDisplayable!Report.cs: Define aReportclass in theReportsnamespace that implements bothIDisplayableandIPrintable. The report should have aTitleproperty (string) and aContentproperty (string), with a constructor that sets both values. Use explicit interface implementation to provide differentRender()behaviors:- When rendered for display:
"[SCREEN] {Title}: {Content}" - When rendered for printing:
"[PRINT] {Title} | {Content}"
- When rendered for display:
Program.cs: In your main file, create aReportusing input values. Since explicit implementations can only be accessed through interface variables, store the same report in both anIDisplayablevariable and anIPrintablevariable, then callRender()on each to show how the same object produces different output based on which interface you're using.
You will receive two inputs:
- Report title
- Report content
Print the output in this format:
Display version:
{Render() via IDisplayable}
Print version:
{Render() via IPrintable}For example, if the inputs are Sales Report and Q4 revenue increased by 15%, the output should be:
Display version:
[SCREEN] Sales Report: Q4 revenue increased by 15%
Print version:
[PRINT] Sales Report | Q4 revenue increased by 15%Notice how the same Report object behaves differently depending on which interface reference you use to access it. This is the power of explicit interface implementation - resolving naming collisions while providing context-appropriate behavior!
Cheat sheet
When a class implements multiple interfaces with members that have the same name, you can use explicit interface implementation to provide separate implementations for each interface.
To explicitly implement an interface member, prefix the member name with the interface name and omit the access modifier:
public interface IAmericanPlug
{
void Connect();
}
public interface IEuropeanPlug
{
void Connect();
}
public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
void IAmericanPlug.Connect()
{
Console.WriteLine("Connected to 120V");
}
void IEuropeanPlug.Connect()
{
Console.WriteLine("Connected to 220V");
}
}Explicitly implemented members can only be accessed through a variable of the interface type, not through the class itself:
var adapter = new UniversalAdapter();
// adapter.Connect(); // Won't compile!
IAmericanPlug american = adapter;
american.Connect(); // Connected to 120V
IEuropeanPlug european = adapter;
european.Connect(); // Connected to 220VThis technique is useful for:
- Resolving naming collisions between interface members
- Hiding interface members from the class's public API when they only make sense in a specific interface context
Try it yourself
using System;
using Reports;
class Program
{
public static void Main(string[] args)
{
// Read input
string title = Console.ReadLine();
string content = Console.ReadLine();
// TODO: Create a Report object with the title and content
// TODO: Store the report in an IDisplayable variable
// TODO: Store the same report in an IPrintable variable
// TODO: Print the display version by calling Render() on IDisplayable
Console.WriteLine("Display version:");
// Print the render result here
// TODO: Print the print version by calling Render() on IPrintable
Console.WriteLine("Print version:");
// Print the render result here
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator