Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C# & .NET (C# 14, .NET 10 LTS)  Module 7 – Inheritance, interfaces and polymorphism

Interfaces, explicit implementation and defaults

Define and implement C# interfaces, keep two contracts apart with explicit implementation, and add default and static abstract members without breaking code.

  • Intermediate
  • 15 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 5 of 15 minutes free
Free: An interface is a contract that any class can sign · Explicit implementation keeps two contracts apart · Interface defaults let you add a member without breaking implementers. With a pass: 3 in-depth parts and 3 interview answers, about 10 minutes.

What you will learn

  • Define an interface and implement several interfaces on one class
  • Use explicit implementation to keep two interfaces' members apart
  • Add a default member to an interface without breaking the classes that implement it
  • Resolve a member that two interfaces both give a default body
  • Declare static abstract members and call them from generic code

Before you start

A checkout offers three ways to ship a 1,200 g parcel, Rs 65 by road, Rs 200 by air and Rs 25 to a pickup point, and it must not care which class works out each price. An interface is the contract that lets it: every class that implements IShippingCalculator promises a Name and a Quote, and the checkout calls those and nothing else.

You will define and implement interfaces, keep two contracts apart with explicit implementation, and add a member to an interface that classes already implement.

An interface is a contract that any class can sign

An interface: A type that declares members without instance state, as a contract. A class or struct that lists it must implement its members, and a class can implement any number of interfaces. declares members but no instance fields or constructors, and a class that lists it after the colon must implement each of its members that has no body. Interface names start with I by convention, and their members are public by default. shipping.cs declares IShippingCalculator and three classes that implement it, and the checkout picks one while the program runs:

shipping.cs: one contract, three classes C# · shipping/shipping.cs
// One contract, three implementations, chosen while the program runs. The rates are made up for this lesson.
IShippingCalculator[] options = [new RoadShipping(), new AirShipping(), new PickupPoint("Koramangala")];
foreach (IShippingCalculator option in options)
{
    Console.WriteLine($"{option.Name,-22} Rs {option.Quote(1200),4}");
}

// The checkout knows only the interface; which class it gets is decided by the customer's choice.
IShippingCalculator chosen = Choose("air");
Console.WriteLine($"chosen: {chosen.Name}, Rs {chosen.Quote(1200)}");

// AirShipping keeps a second contract as well.
ITrackable trackable = new AirShipping();
Console.WriteLine(trackable.TrackingUrl("AW12345"));

static IShippingCalculator Choose(string code) => code switch
{
    "road" => new RoadShipping(),
    "air" => new AirShipping(),
    _ => new PickupPoint("Koramangala"),
};

// What every shipping option promises: a name and a price in rupees for a weight in grams.
interface IShippingCalculator
{
    string Name { get; }

    int Quote(int grams);
}

interface ITrackable
{
    string TrackingUrl(string id);
}

class RoadShipping : IShippingCalculator
{
    public string Name => "Road, 4 to 6 days";

    public int Quote(int grams) => 45 + ((grams - 1) / 500 * 10);
}

// One class, two interfaces, separated by commas.
class AirShipping : IShippingCalculator, ITrackable
{
    public string Name => "Air, next day";

    public int Quote(int grams) => 120 + ((grams - 1) / 500 * 40);

    public string TrackingUrl(string id) => $"https://track.example/air/{id}";
}

class PickupPoint(string area) : IShippingCalculator
{
    public string Name => $"Pickup at {area}";

    public int Quote(int grams) => 25;
}

Output

Road, 4 to 6 days      Rs   65
Air, next day          Rs  200
Pickup at Koramangala  Rs   25
chosen: Air, next day, Rs 200
https://track.example/air/AW12345

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run shipping.cs

The loop sees only the interface, so a fourth way of shipping needs a new class and one more case in Choose, and no change to the code that uses the result. AirShipping implements a second interface, ITrackable, as well: a class has one base class but may implement any number of interfaces. Step through what the checkout knows and what it gets:

Interactive · step by step What the checkout knows, and what it gets Step through shipping.cs. The checkout and the interface stand on the left, the classes that keep the contract on the right.
What the checkout knows, and what it gets, step 5 of 5. Showing The checkout, IShippingCalculator, RoadShipping, AirShipping (TrackingUrl as ITrackable), PickupPoint. In focus: AirShipping.callsThe checkoutloop and ChooseIShippingCalculatorName, Quote(grams)RoadShippingRs 65AirShippingTrackingUrl as ITrackablePickupPointRs 25

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The contract. IShippingCalculator promises two members: a Name and a Quote for a weight in grams. It has no fields and no code of its own.
  2. Three classes sign it. RoadShipping, AirShipping and PickupPoint each list IShippingCalculator after the colon and implement both members, each with its own rate.
  3. The checkout sees the contract. The loop holds each option as an IShippingCalculator and calls Quote(1200). It never names a class, yet each object answers with its own price: Rs 65, Rs 200 and Rs 25.
  4. Chosen at run time. Choose("air") returns an AirShipping as an IShippingCalculator. The checkout calls the same two members and gets Rs 200.
  5. A second contract. AirShipping also implements ITrackable. Through an ITrackable variable the same kind of object offers TrackingUrl, a member the checkout's interface does not have.

Checkpoint

Check: Interfaces as contracts, several per class

  1. Question 1 of 2 A class DroneShipping lists IShippingCalculator after the colon but declares only Quote. What happens?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: It does not compile: the class must implement Name too, because the interface declares it without a body

    Listing an interface is a promise to implement every member it declares without a body; the compiler refuses a class that keeps only part of it.

  2. Question 2 of 2 AirShipping is declared class AirShipping : IShippingCalculator, ITrackable. What can code do with an AirShipping object?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Use it wherever either interface is expected: as an IShippingCalculator and as an ITrackable

    Each interface a class implements is a separate contract, and code sees the members of the type it holds the object as.

Explicit implementation keeps two contracts apart

Payment implements two interfaces that both declare Describe(): the customer’s receipt and the audit log. An ordinary public method would serve both with one body. With explicit interface implementation: Implementing an interface member under the interface's name, as in string IReceipt.Describe(), with no access modifier. It is reached only through the interface, never through the class., the member is named with its interface, string IReceipt.Describe(), has no access modifier, and each interface gets its own body:

receipts.cs: one payment, two Describe methods C# · receipts/receipts.cs
// A payment tells the customer one thing and the audit log another, through two interfaces whose members are both
// called Describe. Explicit implementation gives each interface its own body.
var payment = new Payment("PAY-7781", 1499m, "asha@okbank");

IReceipt receipt = payment;
IAuditEntry audit = payment;
Console.WriteLine(receipt.Describe());
Console.WriteLine(audit.Describe());
Console.WriteLine(Print(payment));

static string Print(IReceipt receipt) => $"printer: {receipt.Describe()}";

interface IReceipt
{
    string Describe();
}

interface IAuditEntry
{
    string Describe();
}

class Payment(string id, decimal amount, string upiId) : IReceipt, IAuditEntry
{
    // No access modifier: an explicit implementation is reached only through its interface.
    string IReceipt.Describe() => $"Paid Rs {amount:F2}, reference {id}";

    string IAuditEntry.Describe() => $"{id} amount={amount:F2} payer={upiId}";
}

Output

Paid Rs 1499.00, reference PAY-7781
PAY-7781 amount=1499.00 payer=asha@okbank
printer: Paid Rs 1499.00, reference PAY-7781

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run receipts.cs

The receipt leaves out the payer’s ID, which only the audit text includes, and Print reaches the receipt’s version because it takes an IReceipt. An explicit implementation is reached only through its interface, never through the class:

explicitcall/Program.cs C# · explicitcall/Program.cs
// receipts.cs, with one more line: Describe called on the Payment itself.
var payment = new Payment("PAY-7781", 1499m, "asha@okbank");
Console.WriteLine(payment.Describe());

interface IReceipt
{
    string Describe();
}

interface IAuditEntry
{
    string Describe();
}

class Payment(string id, decimal amount, string upiId) : IReceipt, IAuditEntry
{
    string IReceipt.Describe() => $"Paid Rs {amount:F2}, reference {id}";

    string IAuditEntry.Describe() => $"{id} amount={amount:F2} payer={upiId}";
}
Building explicitcall Python · explicit_session.py
from session import run

# explicitcall calls Describe on a Payment variable, although Payment implements Describe only explicitly. dotnet run
# builds the project first and stops at the error.
run("""
cd explicitcall
!dotnet run
""")

Output

$ cd explicitcall
$ dotnet run
explicitcall/Program.cs(3,27): error CS1061: 'Payment' does not contain a definition for 'Describe' and no accessible extension method 'Describe' accepting a first argument of type 'Payment' could be found (are you missing a using directive or an assembly reference?) [explicitcall/App.csproj]

The build failed. Fix the build errors and run again.
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 explicit_session.py

CS1061 says that Payment has no Describe at all. Explicit implementation also hides a member from the class’s public surface on purpose, as an IDisposable.Dispose written explicitly does.

Checkpoint

Check: Explicit interface implementation

  1. Question 1 of 2 Payment implements IReceipt.Describe() explicitly. Which call compiles?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: ((IReceipt)payment).Describe()

    Explicitly implemented members belong to the interface: call them through a variable, parameter or cast of the interface type.

  2. Question 2 of 2 Why did Payment implement both Describe methods explicitly in receipts.cs?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The two interfaces declare the same signature but need different text, and only explicit implementation gives each its own body

    Explicit implementation separates two contracts that use the same member name, here a customer's receipt and an audit entry.

Interface defaults let you add a member without breaking implementers

Adding an ordinary member to a published interface breaks every class that implements it: none of them implements the new member, so none of them compiles, as a class that skips an abstract member fails with CS0534. Interface defaults, default interface member: An interface member with a body, allowed since C# 8 on .NET Core 3.0 and later. Classes that do not implement the member use the body; it is called through the interface only. added in C# 8, avoid that: a member with a body is used by every class that does not implement it itself. defaults.cs adds Describe to IShippingCalculator:

defaults.cs: version 2 of the interface, with a default C# · defaults/defaults.cs
// Version 2 of IShippingCalculator adds Describe with a default body. RoadShipping and PickupPoint were written for
// version 1 and compile unchanged; AirShipping writes a Describe of its own.
IShippingCalculator[] options = [new RoadShipping(), new AirShipping(), new PickupPoint("Koramangala")];
foreach (IShippingCalculator option in options)
{
    Console.WriteLine(option.Describe(1200));
}

interface IShippingCalculator
{
    string Name { get; }

    int Quote(int grams);

    // New in version 2: a default implementation, for every class that does not write its own.
    string Describe(int grams) => $"{Name}: Rs {Quote(grams)} for {grams} g";
}

// Written for version 1.
class RoadShipping : IShippingCalculator
{
    public string Name => "Road, 4 to 6 days";

    public int Quote(int grams) => 45 + ((grams - 1) / 500 * 10);
}

// Written for version 2: its own public Describe implements the interface member.
class AirShipping : IShippingCalculator
{
    public string Name => "Air, next day";

    public int Quote(int grams) => 120 + ((grams - 1) / 500 * 40);

    public string Describe(int grams) => $"{Name}: Rs {Quote(grams)}, tracked to your door";
}

// Written for version 1.
class PickupPoint(string area) : IShippingCalculator
{
    public string Name => $"Pickup at {area}";

    public int Quote(int grams) => 25;
}

Output

Road, 4 to 6 days: Rs 65 for 1200 g
Air, next day: Rs 200, tracked to your door
Pickup at Koramangala: Rs 25 for 1200 g

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run defaults.cs

RoadShipping and PickupPoint were written for version 1 and use the default; AirShipping’s own public Describe takes its place. Like an explicit implementation, a default belongs to the interface, not to the class:

defaultcall/Program.cs C# · defaultcall/Program.cs
// defaults.cs, with Describe called on a RoadShipping variable instead of an IShippingCalculator one.
var road = new RoadShipping();
Console.WriteLine(road.Describe(1200));

interface IShippingCalculator
{
    string Name { get; }

    int Quote(int grams);

    string Describe(int grams) => $"{Name}: Rs {Quote(grams)} for {grams} g";
}

class RoadShipping : IShippingCalculator
{
    public string Name => "Road, 4 to 6 days";

    public int Quote(int grams) => 45 + ((grams - 1) / 500 * 10);
}
Building defaultcall Python · default_session.py
from session import run

# defaultcall calls the interface's default Describe through a RoadShipping variable. dotnet run builds the project
# first and stops at the error.
run("""
cd defaultcall
!dotnet run
""")

Output

$ cd defaultcall
$ dotnet run
defaultcall/Program.cs(3,24): error CS1061: 'RoadShipping' does not contain a definition for 'Describe' and no accessible extension method 'Describe' accepting a first argument of type 'RoadShipping' could be found (are you missing a using directive or an assembly reference?) [defaultcall/App.csproj]

The build failed. Fix the build errors and run again.
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 default_session.py

Two more features build on this. When two interfaces both supply a body for one member, the compiler needs a rule to pick one. And a static abstract member: A static member that an interface declares and every implementing type must supply, allowed since C# 11. Generic code calls it on a type parameter, as T.Parse(text). puts a contract on the type itself rather than on its objects. Generic code can call it on a type parameter before it has any object of that type.

Default members need .NET Core 3.0 or later

Microsoft’s history of C# says default interface members need support in the runtime that was added for .NET Core 3.0, so .NET Framework cannot run them, whatever the language version.

Checkpoint

Check: Default interface members

  1. Question 1 of 2 In defaults.cs, which classes used the interface's default body of Describe?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: RoadShipping and PickupPoint

    A default body is used by every implementing class that has no implementation of its own; a class's own public method replaces it.

  2. Question 2 of 2 var road = new RoadShipping(); road.Describe(1200); fails to compile with CS1061. Why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A default member belongs to the interface; RoadShipping does not inherit it as a member of its own

    Like an explicit implementation, a default is reached only through the interface: IShippingCalculator road = new RoadShipping(); compiles and calls it.

Defaults in depth: which body runs, the diamond, and what a default cannot do Pro: opens with a Pro, Premium or Ultimate pass

A member with a default can get its body from three places, and two interfaces can offer two bodies for one member. The runs below show the order the compiler applies, the error when it cannot decide, and the fix.

In this part:

  • The class’s method first, then the most specific interface
  • Two equally specific bodies: CS8705, reproduced and fixed
  • What a default cannot do

3 runnable examples (C#, Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Static abstract members: a contract on the type itself, run in generic code Pro: opens with a Pro, Premium or Ultimate pass

An ordinary interface member promises something about each object; a static abstract member promises something about the type. That lets generic code call a static method of whatever type it is given.

In this part:

  • A loader that creates any type from CSV, through T.FromCsv
  • Chosen by the compiler, not by the object

1 runnable example (C#) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interfaces as seams: a test double in a run, and Java's interfaces compared Pro: opens with a Pro, Premium or Ultimate pass

An interface between two classes is a seam: either side can be replaced without touching the other. The run below replaces an SMS gateway with a fake that a test can read, and the table maps C# interfaces to Java’s for readers who know both.

In this part:

  • A fake gateway that records what would have been sent
  • When an interface is ceremony
  • C# interfaces next to Java’s

1 runnable example (C#) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: fare rules behind one interface

fares.cs checks three fare rules behind IFareRule and its default Describe. Every rule that applies adjusts the fare in turn, in paise, rounded down. All 8 checks fail on the starter:

fares.cs: eight checks of three fare rules C# · practice/fares.cs
// Practice: write three fare rules and the default Describe of IFareRule, then run this file until all eight checks
// pass. The percentages are made up for this exercise and are no railway's or bus company's real rules.
IFareRule[] rules = [new SeniorCitizenRule(), new ChildRule(), new PeakHourRule()];

(string Check, Func<string> Run, string Expected)[] checks =
[
    ("a senior citizen pays 40 % less", () => Apply(new Trip(10_000, 64, 13)), "6000"),
    ("a child pays half", () => Apply(new Trip(10_000, 8, 13)), "5000"),
    ("an adult off peak pays the fare", () => Apply(new Trip(10_000, 30, 13)), "10000"),
    ("peak hours add 10 %", () => Apply(new Trip(10_000, 30, 9)), "11000"),
    ("a senior citizen at peak hours", () => Apply(new Trip(10_000, 64, 18)), "6600"),
    ("paise are rounded down", () => Apply(new Trip(9_999, 64, 13)), "5999"),
    ("Describe has a default", () => rules[1].Describe(new Trip(10_000, 30, 13)), "Child fare does not apply"),
    ("PeakHourRule implements Adjust explicitly", () => (typeof(PeakHourRule).GetMethod("Adjust") is null).ToString(), "True"),
];

int passed = 0;
foreach (var (check, run, expected) in checks)
{
    string got;
    try
    {
        got = run();
    }
    catch (Exception e)
    {
        got = $"throws {e.GetType().Name}";
    }

    bool ok = got == expected;
    if (ok)
    {
        passed++;
    }

    Console.WriteLine(ok ? $"{check}: ok" : $"{check}: FAIL, {got} instead of {expected}");
}

Console.WriteLine($"{passed} of {checks.Length} checks pass");
return passed == checks.Length ? 0 : 1;

// Every rule that applies adjusts the fare, in the order of the rules array.
string Apply(Trip trip) => rules.Where(rule => rule.Applies(trip)).Aggregate(trip.FarePaise, (fare, rule) => rule.Adjust(fare)).ToString();

record Trip(int FarePaise, int PassengerAge, int Hour);

interface IFareRule
{
    string Name { get; }

    bool Applies(Trip trip);

    // The fare in paise after this rule, rounded down to whole paise.
    int Adjust(int farePaise);

    // Give this member a default body: "<Name> applies" or "<Name> does not apply".
    string Describe(Trip trip) => throw new NotImplementedException();
}

// "Senior citizen fare": passengers aged 60 or more pay 40 % less.
class SeniorCitizenRule : IFareRule
{
    public string Name => throw new NotImplementedException();

    public bool Applies(Trip trip) => throw new NotImplementedException();

    public int Adjust(int farePaise) => throw new NotImplementedException();
}

// "Child fare": passengers under 12 pay half.
class ChildRule : IFareRule
{
    public string Name => throw new NotImplementedException();

    public bool Applies(Trip trip) => throw new NotImplementedException();

    public int Adjust(int farePaise) => throw new NotImplementedException();
}

// "Peak hours": trips that start from 8 to 10 or from 17 to 19 o'clock cost 10 % more. Implement Adjust explicitly.
class PeakHourRule : IFareRule
{
    public string Name => throw new NotImplementedException();

    public bool Applies(Trip trip) => throw new NotImplementedException();

    public int Adjust(int farePaise) => throw new NotImplementedException();
}

Output (exit status 1)

a senior citizen pays 40 % less: FAIL, throws NotImplementedException instead of 6000
a child pays half: FAIL, throws NotImplementedException instead of 5000
an adult off peak pays the fare: FAIL, throws NotImplementedException instead of 10000
peak hours add 10 %: FAIL, throws NotImplementedException instead of 11000
a senior citizen at peak hours: FAIL, throws NotImplementedException instead of 6600
paise are rounded down: FAIL, throws NotImplementedException instead of 5999
Describe has a default: FAIL, throws NotImplementedException instead of Child fare does not apply
PeakHourRule implements Adjust explicitly: FAIL, False instead of True
0 of 8 checks pass

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run fares.cs

Write the rules and the default

Copy fares.cs. Give each rule its Name, its condition in Applies and its sum in Adjust (farePaise * 60 / 100 for 40 % off). Implement PeakHourRule’s Adjust explicitly, as int IFareRule.Adjust(int farePaise), and replace the body of Describe in the interface. Run dotnet run fares.cs until it prints 8 of 8 checks pass.

Then practise on contracts, explicit implementation and defaults:

Practice that adapts to you

Practise this lesson

7 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 7 Which of these can a C# interface not contain?

    Choose one answer.

    Show the answer: Question 1 of 7

    Answer: An instance field

    Interfaces declare members and may give them bodies, but they hold no state of each object.

  2. Question 2 of 7 The shop adds DroneShipping, which implements IShippingCalculator. What must change in shipping.cs?

    Choose one answer.

    Show the answer: Question 2 of 7

    Answer: A new class and one more case in Choose; the loop and the code that uses Choose's result stay as they are

    Code that depends on the interface needs no change for a new implementation; only the place that creates objects names the class.

  3. Question 3 of 7 Why does an explicit implementation such as string IReceipt.Describe() have no access modifier?

    Choose one answer.

    Show the answer: Question 3 of 7

    Answer: It is not a member of the class, only of the interface, so public or private would mean nothing; writing one is error CS0106

    An explicit implementation is reached through the interface alone, so the class gives it no access level of its own.

  4. Question 4 of 7 In receipts.cs, Print(IReceipt receipt) is called with the payment. Which body of Describe runs?

    Choose one answer.

    Show the answer: Question 4 of 7

    Answer: IReceipt's: "Paid Rs 1499.00, reference PAY-7781"

    With explicit implementation the interface you call through picks the body, as the third line of the output shows.

  5. Question 5 of 7 When does an interface's default body not run for a class?

    Choose one answer.

    Show the answer: Question 5 of 7

    Answer: When the class implements the member itself

    A default is a fallback: classes that implement the member use their own body, the others the default.

  6. Question 6 of 7 A library must run on .NET Framework 4.8 and on .NET 10. Can its interfaces use default members?

    Choose one answer.

    Show the answer: Question 6 of 7

    Answer: No: default members need runtime support that .NET Core 3.0 added, which .NET Framework does not have

    A library that must also run on .NET Framework adds members another way, such as a new interface or an extension method.

  7. Question 7 of 7 Teams implement your published ISmsGateway, which has Send. Version 2 needs SendBulk. How do you add it without breaking them?

    Choose one answer.

    Show the answer: Question 7 of 7

    Answer: Add SendBulk with a default body that calls Send for each message

    A default body that builds on the existing members is the usual way to grow an interface that others implement.

Interview questions

Fresher · What is explicit interface implementation, and when do you use it?

It is implementing an interface member under the interface’s name, void IAuditEntry.Write(), with no access modifier, so the member is reached only through a variable of that interface type and never through the class. Use it when two interfaces declare members with the same signature that need different bodies, such as a customer’s receipt and an audit entry that both have Describe(). Use it also to keep an interface member off the class’s public surface, when callers should reach it only through the interface. The cost is that code holding the class must cast to the interface first, so keep it for those two cases.

  1. Mid to senior

    Many teams implement your library's public interface IPaymentGateway, which has Charge. You now need Refund. Compare a default member, a new interface IRefundableGateway, and an extension method, and say which you would ship and why.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    After you upgrade two libraries, a class that implements one interface from each stops compiling with CS8705: both interfaces now give the same inherited member a default body. Explain why the compiler refuses, how you fix your class, and what library authors can do to avoid it.

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A codebase has an interface for every class, IOrderService for OrderService and so on, each with one implementation, and a colleague says testing and dependency injection need them. When is an interface worth having, and when is it ceremony? How would you decide for a given class?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • An interface declares members without instance state, and a class implements any number of interfaces, so code that depends on the interface accepts every class that keeps the contract.
  • Explicit implementation gives each interface its own body for a same-named member and keeps the member off the class: CS1061 refuses the call through the class.
  • A default member lets an interface gain a member without breaking implementers; it is used through the interface only, and needs .NET Core 3.0 or later.

Terms in this lesson

default interface member
An interface member with a body, allowed since C# 8 on .NET Core 3.0 and later. Classes that do not implement the member use the body; it is called through the interface only.
explicit interface implementation
Implementing an interface member under the interface's name, as in string IReceipt.Describe(), with no access modifier. It is reached only through the interface, never through the class.
interface
A type that declares members without instance state, as a contract. A class or struct that lists it must implement its members, and a class can implement any number of interfaces.
static abstract member
A static member that an interface declares and every implementing type must supply, allowed since C# 11. Generic code calls it on a type parameter, as T.Parse(text).

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.