How could the function defined in the interface with the body be redefined in the inherited function without overriding?
Asked Answered
Z

3

21

Can somebody explain how same function on same class behaved different?

using System;

public class HelloWorld
{
    public static void Main(string[] args)
    {
        MyUpperScript mAsdfScript = new MyUpperScript();
        IInterface mAsdfInterface = mAsdfScript;
        mAsdfScript.Foo();
        mAsdfInterface.Foo();
    }
}

public class MyUpperScript : MyScript
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
public class MyScript : IInterface
{

}

public interface IInterface
{
    public void Foo()
    {
        Console.WriteLine("Interface");
    }
}

I was expecting it to write "Upper Script" on both Foo() call

Note that if I derive the second class from the interface too it works the way I expect - both print "Upper Script":

// now deriving for the interface too:
public class MyUpperScript : MyScript, IInterface
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
Ziegfeld answered 21/8, 2023 at 22:19 Comment(0)
P
6

I suspect this is because

a class does not inherit members from its interface

(from here), meaning MyScript doesn't actually have a method Foo for MyUpperScript to override. When (and now I'm reallying guessing here...!) you recast the variable to the interface type, the virtual method lookup doesn't know to look for Foo in MyUpperScript anymore, and thus falls back to the default implementation.

It's still weird to me, though, that it doesn't infer that MyUpperScript implements IInterface indirectly - but that might just be that my mental model of how that works has always been wrong, and there's just no opportunity for that to make anything practical difference until you have default interface members. (Before those, all members of implicitly implemented interfaces would also be inherited from the superclass that implements explicitly.) Personally, I think the language would probably make more sense if this was fixed so that both causes behave as if you've specified the interface explicitly (as in your end note), but I haven't been able to find any text that indicates to me whether this was a conscious decision or not, and what the justification was if it was indeed conscious.

If you try to use it on directly on MyScript (as below) you should get a compiler error:

MyScript myScript = new MyScript();
myScript.Foo(); // compiler error here
Patrizius answered 22/8, 2023 at 9:49 Comment(2)
Thanks for answer. So if I want to redefine Foo() from MyUpperScript, is there any other way than defining it in MyScript and overriding MyUpperScript? Like writing public void IInterface.Foo() at MyUpperScript?Ziegfeld
My way to think about default interface methods is that they are extension methods tied to the interface. No inheritance is involved.Eliason
B
3

I would consider your code to be functionally equivalent to the following:

public class MyUpperScript : MyScript
{
    public void Foo()
    {
        Console.WriteLine("Upper Script");
    }
}
public class MyScript : IInterface
{
    void IInterface.Foo()
    {
        Console.WriteLine("Interface");
    }
}

public interface IInterface
{
    void Foo();
}

Note that MyScript explicitly implements IInterface.Foo(). So there is no Foo implementation on MyScript and thus MyUpperScript is free to implement its own Foo().

Blotchy answered 22/8, 2023 at 10:7 Comment(0)
V
2

This behavior is by design, and it's so that default interface members behavior is consistent with abstract interface members. Consider this variation of your example:


public class HelloWorld
{
    public static void Main(string[] args)
    {
        MyUpperScript mAsdfScript = new MyUpperScript();
        IInterface mAsdfInterface = mAsdfScript;
        mAsdfScript.Foo();
        mAsdfInterface.Foo();
    }
}

public class MyUpperScript : MyScript
{
    public new void Foo() => Console.WriteLine("Upper Script");
}
public class MyScript : IInterface
{
    public void Foo() => Console.WriteLine("MyScript");
}

public interface IInterface
{
    public void Foo();
}

The output is the same:

Upper script
MyScript

First, note that the new modifier is required on MyUpperScript.Foo(). Even if MyScript explicitly declares that it implements IInterface, the new modifier is required. That's because interface members aren't virtual by default. MyScript.Foo() doesn't require the override keyword because it's not overriding any method; it's implementing an interface.

Adding a default implementation for IInterface.Foo() doesn't change any of those rules. If you add the default implementation for IInterface.Foo(), yet keep the implementation in MyScript, the compiler warns you that you should add the new modifier on MyUpperScript.Foo(). Without an implementation of MyScript.Foo(), MyUpperScript.Foo() no longer emits a warning. It implements the interface member.

This behavior ensures today design goals are met:

  1. Adding a default interface member to an interface that has already been released won't break any existing code. That's true even if a class that implements the modified interface, even indirectly, already includes a method with the same signature.
  2. The behavior for implementing or re-implementing a default interface member is as close as possible to implementing or reimplementing an interface member without a default implementation.
Vacillate answered 28/8, 2023 at 14:15 Comment(1)
The OP's code isn't the same as new void Foo(). You're introducing code that doesn't replicate what the OP is seeing. It's kind of like you're answering a similar question but not the OP's one.Blotchy

© 2022 - 2024 — McMap. All rights reserved.