1

I try to use the new C# default interface methods. Can anyone explain the following code and why this happens?

Minimal test example:

public static void Main()
{
    Class c = new Class();
    IInterface i = new Class();
    c.M1();
    i.M1();
    (i as IInterface).M1();
    Class2 c2 = new Class2();
    Class2Base c2b = new Class2();
    c2.M2();
    c2b.M2();
    (c2 as Class2Base).M2();
}

public interface IInterface
{
    public void M1() => Console.WriteLine("Call from interface");
}

public class Class : IInterface
{
    public void M1() => Console.WriteLine("Call from class");
}

public class Class2 : Class2Base
{
    public void M2() => Console.WriteLine("Call from class2");
}

public class Class2Base
{
    public void M2() => Console.WriteLine("Call from class2base");
}

Outputs:

Call from class
Call from class
Call from class
Call from class2
Call from class2base
Call from class2base

I expect to see "Call from interface" in the second call similar to when using normal inheritence like in the second example.

To summarize:

Why is the interface method hidden and (maybe) how can i still call it.

Also, this is my first question here so any tips are welcome

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Limited
  • 15
  • 4
  • 3
    The culprit is "default". `Class` implements `M1`, so it is called. If you make another class that "implements IInterface" but in which you _do not_ implement `M1`, then the _default_ from the interface is called. It has only marginally to do with "classic" inheritance. – Fildor Apr 21 '20 at 15:53
  • Just as a side note, albeit for Java, still oop, --> https://stackoverflow.com/questions/36335838/why-is-adding-default-methods-to-interfaces-in-java-8-a-good-design-choice-and-w#comment60295932_36335838 – Soner from The Ottoman Empire Apr 21 '20 at 16:04
  • A way to wrap your head around why this is different is to explore why this feature was added. Without it, you cannot add methods to your interface, without breaking your existing code. If you don't have that feature and add a function, you need to touch _ALL_ implementations. Depending on how that implementation was done, this can range from easy-peasy to "just no". Having it, you can at least add the new function with a "dummy" non-breaking implementation and have all new implementations support a new feature. Neat, innit? – Fildor Apr 21 '20 at 16:07
  • 2
    I note that you should have gotten a warning on `class2` noting that there is a possible unintentional shadowing there. If you intend there to be two non-virtual methods with the same name, the convention is to tell the compiler that this is not a mistake by decorating the shadowing method with `new`. If you intend the shadowing method to be a virtual override, you need to mark the original method as `virtual` and the new method as `override`. – Eric Lippert Apr 21 '20 at 16:08
  • @Related: https://stackoverflow.com/a/160095/4990642 , think it as "being used _new_" – Soner from The Ottoman Empire Apr 21 '20 at 16:09
  • 1
    I understand though why this is a little confusing. Methods which implement interface members *behave* in some ways as though they are virtual, and in fact if you look at the generated code, they *are* virtual, but for the purposes of the C# language's rules, they are not treated as virtual, and that can be a little confusing. – Eric Lippert Apr 21 '20 at 16:11
  • @Fildor Never thought about it in that way. I was sort of hoping it was basically multiple inheritance in the same way you inherit classes. – Limited Apr 21 '20 at 18:44
  • It should not be *too* surprising that interface multiple inheritance is different than class multiple inheritance *because C# does not implement class multiple inheritance*. If interface multiple inheritance worked *exactly the same* as class multiple inheritance then class multiple inheritance would likely have been implemented *at the same time as this feature*! – Eric Lippert Apr 21 '20 at 19:00
  • Interface multiple inheritance with default implementations behaves a lot like *trait inheritance* that we see in some other OO languages. We could have a debate about the subtle ways in which default interface implementations differ from the more formal understanding of traits, but, maybe let's not. – Eric Lippert Apr 21 '20 at 19:01

1 Answers1

3

Why is the interface method hidden?

Default interface methods are just that. The method in the interface is the default implementation if no other implementation is provided.

In every one of your cases, you provided an implementation, so the default will not be used.

When you provide a non-default implementation, we assume that the new implementation is better than the default implementation; if it was not better, you would not have provided it, right? You would not have done work for no reason, and presumably you want your work to be used. It would be foolish for the runtime to call the default implementation when a better implementation exists.

How can I still call it?

In your code, you don't.

If you want to always have a default implementation that you can call, you can do something like:

public interface IInterface
{
    public void M1() => MyHelperMethods.M1(this);
}

public static class MyHelperMethods
{
    public static void M1(IInterface i) => 
      Console.WriteLine("Call from static");
}

And now if you want to call the default implementation you can call MyHelperMethods.M1 directly.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I recently reread your blog post on [FAIC](https://ericlippert.com/category/brittle-base-classes/) where you describe why implementing an interface that is less accessible than a public class is ok, but the same does not hold true for base classes. Does this feature change your assessment on that? – Justin Blakley Apr 30 '20 at 14:32