🔧 The Default Interface Method That Only Worked Through One Door
A default interface method was added specifically so an interface could grow a new member without breaking every class that already implements it – any class that doesn’t override the member simply falls back to the body written on the interface itself. What catches people off guard is that this fallback is only reachable through a variable typed as the interface. The same object, accessed through a variable typed as the implementing class, doesn’t see that member at all – not as inherited, not as hidden, not as anything – because the compiler resolves it based entirely on the compile-time type doing the asking.
🔎 The Problem
public interface IGreeter
{
string Greet() => "Hello from the default!";
}
public class EnglishGreeter : IGreeter
{
// Deliberately doesn't override Greet() - relies on the default.
}
var greeter = new EnglishGreeter();
Console.WriteLine(greeter.Greet()); // Compile error: EnglishGreeter
// has no member called Greet() -
// the default implementation is
// only visible through IGreeter.
IGreeter asInterface = greeter;
Console.WriteLine(asInterface.Greet()); // "Hello from the default!"
// same object, different
// compile-time view.
✅ Fix: Call It Through the Interface, or Implement It Explicitly
- Call default interface members through an interface-typed reference (or variable), not through the concrete class type, since the compiler resolves them based on the compile-time type only.
- Explicitly implement (override) the member in the class whenever it genuinely needs to be callable through the class’s own type, rather than relying on the default being reachable from anywhere.
- Treat a default interface method as an opt-in fallback for interface consumers specifically, not as a free method that automatically becomes part of every implementing class’s own public surface.
⚠️ Why This Is Easy to Miss
- The class compiles cleanly and the interface compiles cleanly; the only symptom is the missing member when accessed through the class type, which reads exactly like a member that was simply never written rather than one that exists but is inaccessible from that angle.
- This is a different situation from an abstract base class with a virtual method, which is callable through the derived type – the comparison to familiar inheritance rules leads most C# developers to assume behavior default interface methods don’t actually have.
A default interface method isn’t part of the class that implements it – it’s part of the interface, visible only to whoever’s holding that interface, and nothing else.
