Skip to content

Overload resolution puzzler

Here is a bit of a puzzle – what does the following program print?

using System;
class Parent { public void Foo(String str) { Console.WriteLine("Parent.Foo"); } } class Child : Parent { public void Foo(Object str) { Console.WriteLine("Child.Foo"); } } class Example { static void Main() { Child child = new Child(); child.Foo("test"); } }

Given the fact that this article is billed as a “puzzler” I am sure that you correctly deduced that this program does not do the obvious thing (that is to say it does not print "Parent.Foo"). This program prints "Child.Foo".

Now this does not seem to make very much sense. Everything that we know about C# tells us that overload resolution always chooses the best method possible for the call site based on the types of the arguments passed. Since in this case we are passing a string to the method group Foo we would think that the compiler would choose the method that actually accepts a string.

A bit of research shows that this functionality was by design. Section 7.4.2 (Overload resolution) of the C# Specification calls this compiler behavior out specifically:

[M]ethods in a base class are not candidates [for method invocation] if any method in a derived class is applicable.

So now we can explain this strange behavior – since Child derives from Parent none of Parent's methods will ever be considered candidates for overload resolution by the compiler. While this is all well and good it does not help us understand why this decision was made. Why was the language designed this way?

I believe that this part of the C# specification was written to prevent owners of a base class from changing (accidentally or otherwise) the behavior of any derived class simply by changing the method group members of a given virtual method. Without section 7.4.2 the base class’s implementation of Foo would have been chosen as it’s parameter type is a better match to the type of the argument at the call site. However this would mean that if the owner of the base class were to add a new overload to a method group at any time that was a better match the compiler would silently choose the better overload and change the behavior of the code at that call site.

It seems as though this restriction on method overload resolution is in place to protect the owners of derived classes. We can be confident that classes from which we derive cannot maliciously or accidentally change the behavior of our code.

5 Comments

  1. Huh

    So this means that i can override ANY method, just by using object as parameter(s) and then casting it to whatever I’d like inside the function?

    Posted on 08-Nov-10 at 11:51 am | Permalink
  2. @Huh – Yes, you can overload any method on a base class this way (not override – overriding a method is different).

    Posted on 08-Nov-10 at 12:14 pm | Permalink
  3. This behavior may be by design, but I’d argue it’s a bad design. For one thing, if you’re worried that a base class might be trying to maliciously change the behavior of your program, then you have no right to be using that base class in the first place. If a base class wanted to do something malicious, there are a lot of easier ways it could go about that without relying on this particular behavior – it could simply change some of the base class behavior that we do rely on, since presumably we are deriving from this base class for a reason. So I think that’s a false argument. The more realistic argument is that the base class could accidentally change some behavior of the derived class. That is indeed a possible problem. However, there is another possible problem which is just as bad – ie., that the derived class could accidentally change some behavior of the base class. And the decision to implement overloading in this way actually makes it much more likely that just this situation could occur. By implementing this in a counter-intuitive way, the language designers increase the probability that the derived class might override some base-class behavior that it meant to preserve. So I’d say that in the best case this is a net neutral – but since it is more counter-intuitive, I’d call it a net negative.

    Posted on 08-Nov-10 at 1:14 pm | Permalink
  4. Pepe

    I thought the obvious thing would indeed be that the program would print “Child.Foo”. The resolver can’t try to determine what the “best” method is based on parameter type, after all, what if the parent method was “Parent.Foo(String a, Object b)” and the child method was “Child.Foo(Object a, String b)”? There would be no way to ever determine that one method matched a call better than the other simply by parameter types.

    So instead, for a given method call, the resolver instead looks through the class hierachy, starting with the object’s class then UP-ward, and uses the first method that matches (even if some ancestor method might match “better”). So child methods can indeed hide parent methods (I think the compiler will issue a warning when compiling the Child class). A caller could type cast the child object to Parent when calling Foo in order to call Parent.Foo rather than Child.Foo.

    Posted on 08-Nov-10 at 2:22 pm | Permalink
  5. More interesting is the resolution of:

    Parent child = new Child();
    child.Foo("test");
    

    You need to cover this for a comprehensive foray into the issue.

    Posted on 23-Nov-10 at 9:16 pm | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*