0

I have a problem with my code.

I would expect that since I'm constructing the Implementation object; every time I call Method() I'd use actual Implementation.Method() and not it's abstract's Base.Method(). It does not seem reasonable that I have to downcast to actual implementer or specify interface explicitly (So interfaces are not transitive in C#? I will call the "first proper instance of interface implementer" and not my class?)

I have a structure similar to this (simplified for clarity): https://dotnetfiddle.net/oYVlQO

using System;

public interface IBase
{
    string Method();
}

public abstract class Base : IBase
{
    public string Method() { return "Sample"; }
}

public class Implementation : Base // if I add ", IBase" to this it works as expected, but why?
{
    new public string Method() { return "Overriden"; }
}

public class Program
{
    // and it's used like so...
    public static void Main()
    {
        IBase b = new Implementation();
        //Implementation b = new Implementation(); // It works as expected, always using Implementation.Method();
        Console.WriteLine(b.Method()); // Produces "Sample", so Base.Method(). Why not implementation?
        Console.WriteLine(((Implementation) b).Method()); // Produces "Overriden", so Implementation.Method(); Since when I have to downcast to use overriden method!?
    }
}
}

I'm really scratching my head over this; Especially that the same code in Java works "as I would expect" https://repl.it/repls/VitalSpiritedWebpage

I've tried to find it in the c# specs to no avail, maybe I do not have the proper keywords...

Jacek Lipiec
  • 412
  • 3
  • 10
  • 5
    `Method()` in your abstract `Base` class should be marked `virtual`, and then instead of using `new public method` in your `Implementation`, do `public override string Method()`. `new` is providing a new implementation of the method, hiding the one in the base class. `virtual` + `override` actually overrides the method. – Cᴏʀʏ May 07 '19 at 20:57
  • @Cᴏʀʏ It does work, thank you. So I assume that the situation occurs due to this? "When a virtual method is invoked, the run-time type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member." So how should I approach the code in which (consumed) abstract class has it's method NOT marked as virtual, how can I override them? Only via downcasting? – Jacek Lipiec May 07 '19 at 21:01
  • 3
    Using the `new` modifier is **NOT** some form of overriding. Using the (optional) `new` modifier is member hiding (also called member shadowing). Overriding and hiding are two **entirely different** things! –  May 07 '19 at 21:01
  • "_Especially that the same code in Java works "as I would expect"_" Only if you were to treat C# as just being some Java dialect... (\*cough\*) ;-P –  May 07 '19 at 21:07
  • 3
    If a member is not marked `virtual` or `abstract`, it cannot be *overridden* because the run-time type of the object is not checked when calling it (there's no "dynamic dispatch"). The implementation to call is determined only by the declared type of the reference. In java, members are virtual by default - in C#, they are not. – Blorgbeard May 07 '19 at 21:09
  • Thank you all :) Well, it shows that I code mostly in Java. After some read-up (https://stackoverflow.com/questions/1853896/is-it-possible-to-override-a-non-virtual-method) I can now understand at least why it behaves is as such and how to work around this issue correctly (via composition). – Jacek Lipiec May 07 '19 at 21:12
  • You added the `new` keyword when the compiler told you that you did it wrong. That warning is accurate 99% of the time, you have to be sure. After you did that, the class has *two* methods named Method(), the inherited one is hidden and hard to get to without a backdoor, the interface provided one. You might as well have named it OtherMethod() and not get the warning. Note that C# is different from Java, methods are only virtual when you declare them that way. In Java all methods are virtual. – Hans Passant May 07 '19 at 21:21

3 Answers3

2

In cause of the question, which is: Why is it that way? My answer: Because you don’t override the method but hide it. The interface is implemented by the baseclass, so the Method is called on the base-class. To answer the question, which isn’t asked: How would it work?

Answer:

using System;

public interface IBase
{
    string Method();
}

public abstract class Base : IBase
{
    public virtual string Method() { return "Sample"; }
}

public class Implementation : Base 
{
     public override string Method() { return "Overriden"; }
}
Nikolaus
  • 1,859
  • 1
  • 10
  • 16
  • Well, you adapted the code from the question, but you left the comments referring to the original code there untouched. For some, the behavior of your code vs. the comments in your code can make your code example here somewhat confusing... –  May 07 '19 at 21:06
  • @elgonzo Thanks for pointing that out. I always use the original code, to be nearest to OP‘s needs, but the comments should have been removed. Updated. – Nikolaus May 07 '19 at 21:21
1

You may want to take a look at the part of the C# spec that deals with interface re-implementation.

When you access a member through the interface, it begins its lookup at the most derived type that explicitly implements that interface. In your example, the most derived type is Base and so it calls the method that's present there.

When you added IBase to the list of interfaces explicitly implemented by Implementation it worked, because that's the new starting point for lookup and it finds your new method.

You can either solve your problem by making the base member virtual and then overriding it in derived classes, or you can re-implement the interface by including that in the list for your Implementation class.

Chris Hannon
  • 4,134
  • 1
  • 21
  • 26
  • Interface re-implementation does not apply to the problem here at hand (as it would require the `Implementation` class to include the interface `IBase` in its base class list; which it does not do). –  May 07 '19 at 21:16
  • @elgonzo You are correct, I've done this hastily. I'll probably do my own write-up based on your responses, thank you – Jacek Lipiec May 07 '19 at 21:18
  • 1
    @elgonzo You're welcome to take a look at the comment included beside the `Implementation` type in @Jacek's question. That is explicitly the result of interface re-implementation and answers the question. – Chris Hannon May 07 '19 at 21:20
  • Well, actually, hold on. I just now noticed the question in the comment there with regard to IBase for Implementation. So, i guess keep your answer. Perhaps just make it a little clearer that you refer to the "code comment question" that could be easily overlooked... –  May 07 '19 at 21:20
  • @ChrisHannon, yeah, i wrote my last comment while you wrote your comment ;-) –  May 07 '19 at 21:21
1

So the problem in my sample code is two-fold.

I assumed that in C# methods are "virtual" by default (as is in Java). Since I'm usually coding in Java, I've made an incorrect assumption.

See Is it possible to override a non-virtual method?

If I'd use virtual, I could override the method and achieve exactly the output I expected, as described in doc:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/virtual namely "When a virtual method is invoked, the run-time type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member."

My code, hovewer, is using method hiding, so unless I inform the compiler about my intention of using my implementation, it'll default to non-hidden method (as resolved by abstract class being the actual, original implementer)

Jacek Lipiec
  • 412
  • 3
  • 10