6

I have simplified my code to this

 internal class Program
{
    private static void Main(string[] args)
    {
        Child d = new Child();
        int i = 100;
        d.AddToTotal(i);

        Console.ReadKey();
    }

    private class Parent
    {
        public virtual void AddToTotal(int x)
        {
            Console.WriteLine("Parent.AddToTotal(int)");
        }
    }

    private class Child : Parent
    {
        public override void AddToTotal(int number)
        {
            Console.WriteLine("Child.AddToTotal(int)");
        }

        public void AddToTotal(double currency)
        {
            Console.WriteLine("Child.AddToTotal(double)");
        }
    }
}

The issue is that this calls

public void AddToTotal(double currency)

although I am calling it with an int and it should be using

public override void AddToTotal(int number)

Using the parent returns the expected result.

 Parent d = new Child();
 int i = 100;
 d.AddToTotal(i);

Update:

Thanks to @Jan and @azyberezovsky for pointing me to the specification. I have added a virtual empty method to the base class to get around this for now.

Raj Ranjhan
  • 3,869
  • 2
  • 19
  • 29
  • 2
    Well, I was able to replicate the results, and ensure that it wasn't just a typo in which the method name of the int overload was different. – Servy Mar 28 '12 at 15:08
  • 1
    `d.AddToTotal(number:i)` calls the int overload, as expected, but you shouldn't have to do that... – Servy Mar 28 '12 at 15:12
  • 3
    See this answer for an explanation: http://stackoverflow.com/a/1833268/25727 – Jan Mar 28 '12 at 15:12
  • Is this method hiding? Can you hide a base method if the signature isn't the same? – Matt Burland Mar 28 '12 at 15:12

2 Answers2

8

A member lookup of a name N in a type T is processed as follows:

First, the set of all accessible members named N declared in T and the base types of T is constructed. Declarations that include an override modifier are excluded from the set. If no members named N exist and are accessible, then the lookup produces no match, and the following steps are not evaluated.

Thus when you use variable of Child type

Child d = new Child();
int i = 100;
d.AddToTotal(i);

method public override void AddToTotal(int number) is excluded from set, and we have only one method with name N left. Int is converted implicitly to double, so no errors occured.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • I find it very odd that overload resolution would give a higher priority to new methods over overridden methods, but apparently it does...odd. Anyone know what the reasoning behind that is? – Servy Mar 28 '12 at 15:17
  • 1
    @Servy see this answer from Eric Lippert for the reasoning: http://stackoverflow.com/a/1833866/53777 – Dan Rigby Mar 28 '12 at 15:24
  • 4
    @Servy: Many people find this choice odd, but there are two very good reasons for it. The first reason is that *the developer who wrote the derived class method had more information than the developer who wrote the base class method.* Specifically, the derived class developer knows what the implementation details of the derived class are, but the base class developer does not. Overload resolution should choose the method that was written by the guy who knew most about the object that the user is actually using. – Eric Lippert Mar 28 '12 at 15:27
  • @Servy: Second, it prevents a form of the brittle base class failure. See http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx for details. – Eric Lippert Mar 28 '12 at 15:27
  • @EricLippert Thanks for the further clarifications. – Servy Mar 28 '12 at 15:32
  • @lazyberezovsky: Your analysis is not quite correct. A correct analysis is: member lookup produces a method group with *two* members: Parent.Add(int) and Child.Add(double). The Child.Add(int) method is ignored by lookup. Then overload resolution determines that there is an *applicable* member in the method group that is in Child, and it therefore ignores all members of the method group that were declared in Parent. – Eric Lippert Mar 28 '12 at 15:36
  • @Eric Lippert, thanks for your great comments! It's much better than boring C# spec :) Btw can you look through question http://stackoverflow.com/questions/9890609/c-sharp-selecting-a-different-method-to-the-one-expected-with-optional-parameter ? That's really strange behavior for me :) – Sergey Berezovskiy Mar 28 '12 at 15:45
  • @lazyberezovsky: That's *exactly* the same situation. Member lookup ignores the overriding methods. There are two non-overriding methods in the method group, one declared in A and one declared in B. The one declared in B is *applicable*, and therefore nothing declared in A is even considered. That reduces the applicable candidate set to a single member, which is chosen. – Eric Lippert Mar 28 '12 at 15:49
  • @Eric Lippert Well yes, I agree about same algorithm. But it looks like "brittle base class problem" you described in your blog.. – Sergey Berezovskiy Mar 28 '12 at 15:52
  • @Eric Lippert and one more question :) If we remove virtual method from class B, why member lookup does not ignore override method on class C? Thanx! – Sergey Berezovskiy Mar 28 '12 at 15:53
  • @lazyberezovsky: Ah, I see your point: `class A { public virtual void M() {} } class B : A { } class C : B { public override void M() {} }` and now someone adds `public void M(int x = 0){}` to B, and suddenly overload resolution on `c.M()` goes from choosing `A.M` (which actually calls `C.M`) to choosing `B.M`. Yep, that's a brittle base class failure all right. C# does not eliminate all of them, it just mitigates as many as we reasonably can. – Eric Lippert Mar 28 '12 at 15:58
  • @lazyberezovsky: I don't understand your second question; it *does* ignore the override method on C. – Eric Lippert Mar 28 '12 at 15:59
  • @Eric Lippert, imagine we have `class A { public virtual void M(){}} class B : A { public override void M()}`. And when we call `B b = new B(); b.M();` why override method is not ignored in this case? – Sergey Berezovskiy Mar 28 '12 at 16:04
  • Ah, I see.. it's because this method is only method in class interface :) My bad – Sergey Berezovskiy Mar 28 '12 at 16:08
  • @EricLippert One more in your queue . If I remove "double" method and write public void AddToTotal(string currency), then it calls "int" one. I have question , why it has to neglet "string" method here ? (Please note that I am relating to original question of this page) – Dhananjay Mar 29 '12 at 04:06
  • @dnkulkarni: Because a method that took a string would not even be *applicable*. A method in a derived class only takes precedence over a method in a base class if it is applicable in the first place! – Eric Lippert Mar 29 '12 at 04:44
  • @EricLippert, Ok then here is a case: I have 3 methods in a class called School out of which first is overriden. 1> override Educate( Me a) 2> Educate(MyKid a) 3> Educate(MyGrandKid a). And Fortunately MyGrandKid is derived from MyKid , MyKid is derived from Me. Now if somebody says var mit = new School(); And then say mit.Educate(new Me()); Which method should be called ? Rather why ? (I have read Brittle class problem but there is no such hierarchy definition, it says resolution between int , float.. which are not in parent child relation) – Dhananjay Mar 29 '12 at 04:58
  • @dnkulkarni: Since only the first of those methods is *applicable*, obviously it would be the one chosen. – Eric Lippert Mar 29 '12 at 05:01
  • @dnkulkarni: That int and float are not in a base-type/derived-type relationship is correct but completely irrelevant. What matters is not whether the types have a base/derived relationship; what matters is whether the types are implicitly convertible to each other. int is implicitly convertible to float; float is not implicitly convertible to int, and therefore int is the more specific type. – Eric Lippert Mar 29 '12 at 05:03
  • @EricLippert Very interesting. What I found more is when I do this : Me dnk = new MyGrandKid(); (new school()).Educate(dnk) ; It looks for compiletime type of dnk and calls Educate (Me a) method rather than Educate (MyGrandkid a). But when i say var dnk = new MyGrandKid() then it calls Educate (MyGrandKid a). Why would it be thinking to take compile time type ? Forgive me if my words seems more of "dispute" type. Please cast them into "request" type as runtime type is that only :) – Dhananjay Mar 29 '12 at 05:21
  • 1
    @dnkulkarni: The *compiler* chooses which virtual method slot to invoke at *compile time*, so how could it use anything other than the *compile time* type? If you want to use the *runtime* type then type everything as "dynamic". – Eric Lippert Mar 29 '12 at 05:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9444/discussion-between-dnkulkarni-and-eric-lippert) – Dhananjay Mar 29 '12 at 05:28
1

This answer to this question explains the technical reasons why this happens. I have included the answer inline here for convenience, but all credit is due to tvanfosson.

See the section of the C# Language Specification on Member Lookup and Overload Resolution. The override method of the derived class is not a candidate because of the rules on Member Lookup and the base class method is not the best match based on the Overload Resolution rules.

Section 7.3

First, the set of all accessible (Section 3.5) members named N declared in T and the base types (Section 7.3.1) of T is constructed. Declarations that include an override modifier are excluded from the set. If no members named N exist and are accessible, then the lookup produces no match, and the following steps are not evaluated.

Section 7.4.2:

Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way, as described in detail in the sections listed above. For example, the set of candidates for a method invocation does not include methods marked override (Section 7.3), and methods in a base class are not candidates if any method in a derived class is applicable (Section 7.5.5.1). (emphasis mine)

Community
  • 1
  • 1
Dan Rigby
  • 17,133
  • 6
  • 43
  • 60