0

I encountered a bug in my code today, highlighting the fact that apparently I don't understand polymorphism as well as I thought. Here's some sample code that illustrates my error:

internal class Startup{
    interface IInterface{
        bool SomeProp{get;}
    }
    class ClassA:IInterface{
        public virtual bool SomeProp{get{return true;}}
    }
    class ClassB:ClassA{
        public override bool SomeProp{get{return false;}}
    }
    public static void Main(String[] args){
        IInterface test=new ClassB();
        bool prop=test.SomeProp;
        Console.WriteLine("result="+prop);
    }
}

This shows a result of false, exactly as I'd expect. But consider this code:

internal class Startup{
    interface IInterface{
        bool SomeProp{get;}
    }
    class ClassA:IInterface{
        public virtual bool SomeProp{get{return true;}}
    }
    class ClassB:ClassA{
        public virtual bool SomeProp{get{return false;}}
    }
    public static void Main(String[] args){
        IInterface test=new ClassB();
        bool prop=test.SomeProp;
        Console.WriteLine("result="+prop);
    }
}

In this code, I accidentally typed the word "virtual" instead of "override" in ClassB. This was a typeo, but still, I would've expected one of the following things to happen:

(1) Compiler error, telling me that if I really wanted to create a new virtual property called SomeProp at the level of ClassB, I should've used the 'new' keyword.

or

(2) Complete passthrough of functionality: false would be returned by way of ClassB.

... the one thing I would NOT have expected is for true to be returned. Could someone explain what's happening in the second example with the typeo? Should I be using the virtual keyword at all in Class A? I would've though I had to, if I wanted its descendent ClassB to be able to override it.

Festus Martingale
  • 539
  • 1
  • 3
  • 12
  • As an added note: adding the `new` modifier to `public virtual bool SomeProp` in ClassB isn't required, it just adds intent – Camilo Terevinto Mar 04 '19 at 18:53
  • 1
    The question is strange. In point one you say that you expected a compiler error. Well, when I try to compile your code I get a compiler warning saying exactly that: **To make the member override, add the override keyword, otherwise add the new keyword**. You should pay attention to compiler warnings. If you have warnings turned off, *turn them back on*. – Eric Lippert Mar 04 '19 at 21:13
  • 1
    The reason this is a warning and not an error is because the scenario is most commonly seen when someone *changes the contents of the base class*, and the derived class author needs to be warned that they are now shadowing where they probably intend to be overriding. We don't want a brittle base class failure to become an error, since that leads to too many "breaking change" scenarios. – Eric Lippert Mar 04 '19 at 21:14
  • Thanks, Eric. Yeah, I'm not getting that warning even after clean/rebuild. I certainly wouldn't have gone out of my way to manually turn that warning off but I'll investigate further. I likely would've caught my typo early on had I seen that warning [output window is configured to show both errors and warnings]. What would be the intention of declaring a property (SomeProp in ClassB, for example) as virtual when it's *already* marked as virtual in a base class? In my case, it was a typeo but presumably there's a reason that someone might do this. – Festus Martingale Mar 04 '19 at 21:31
  • 1
    What version of C# are you using? That warning was added in C# 1.0, so it should still be there. – Eric Lippert Mar 05 '19 at 00:51
  • There is almost no reason why you'd do that deliberately. Again, **reason about it from the perspective of the brittle base class**. The way this scenario typically arises is alpha team makes `public class Alpha{}`, bravo team makes `public class Bravo: Alpha { public virtual void M() { } }` and then alpha team says, oh, that's a great idea, they add `public virtual void M()` to `Alpha`, and now when `Bravo` is recompiled we have two methods both called `M`, both virtual, one hiding the other. Since that is probably wrong, you get a warning. – Eric Lippert Mar 05 '19 at 00:54
  • I figured out the reason the warning wasn't showing up. Thanks for pointing that out and thanks for actually answering the question. Best.. – Festus Martingale Mar 05 '19 at 01:41

0 Answers0