16

Could anyone be so nice and explain me why this code shows Derived.DoWork(double). I can come up with some explanations for this behaviour, however I want someone to clarify this for me.

using System;

public class Base
{
    public virtual void DoWork(int param) {
        Console.WriteLine("Base.DoWork");
    }
}

public class Derived : Base
{
    public override void DoWork(int param) {
        Console.WriteLine("Derived.DoWork(int)");
    }

    public void DoWork(double param) {
        Console.WriteLine("Derived.DoWork(double)");
    }

    public static void Main() {
        int val = 5;
        Derived d = new Derived();
        d.DoWork(val);
    }
}
mic4ael
  • 7,974
  • 3
  • 29
  • 42
  • See [7.3 Member lookup](http://msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx) 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. – Habib May 12 '14 at 17:49

3 Answers3

10

Eric lippert used to say always "Closer is better".

A method first declared in a derived class is closer than a method first declared in a base class.

So from the above link, derived class is closer hence that is chosen.

This behavior is carefully implemented to avoid Brittle base class problem

For completeness I'll share the bullets:

  • A method first declared in a derived class is closer than a method first declared in a base class.

  • A method in a nested class is closer than a method in a containing class.

  • Any method of the receiving type is closer than any extension method.

  • An extension method found in a class in a nested namespace is closer than an extension method found in a class in an outer namespace.

  • An extension method found in a class in the current namespace is closer than an extension method found in a class in a namespace mentioned by a using directive.

  • An extension method found in a class in a namespace mentioned in a using directive where the directive is in a nested namespace is closer than an extension method found in a class in a namespace mentioned in a using directive where the directive is in an outer namespace.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • That is exactly the answer I was thinking about! – mic4ael May 12 '14 at 17:46
  • He is passing an integer into `d.DoWork` so it should be printing "Derived.DoWork(int)", not "Derived.DoWork(double)". – Trisped May 12 '14 at 17:47
  • See also http://stackoverflow.com/questions/1451099/how-to-override-an-existing-extension-method/1451184#1451184 – John Saunders May 12 '14 at 17:48
  • I'd still expect a method with an exact method signature match to be closer than an overload on the derived class with a slightly different signature. Live and learn I guess. Now I have to see what VB does... – Bradley Uffner May 12 '14 at 17:48
  • @Trisped Read this carefully A method first declared in a derived class is closer than a method **first declared in a base class**, actual method is first declared in base class only so I guess it is not closer. – Sriram Sakthivel May 12 '14 at 17:51
  • 1
    @Trisped, although `Derived` overrides `DoWork(int)`, the compiler still considers this method to belong to `Base`. `DoWork(double)` is compatible, as an `int` is __implicitly__ being cast to `double`, and is also defined in `Derived`, so it is "closer". As a proof, if you change in `Derived` to `new public void DoWork(int)`, you will see that the `new` keyword tells the compiler to treat `DoWork(int)` as being now owned by `Derived` and will match it instead. – Ivaylo Slavov May 12 '14 at 17:51
  • @BradleyUffner It works this way to avoid the brittle base class problem. Check out the updated link. – Sriram Sakthivel May 12 '14 at 17:56
  • 2
    I tested it, and in case anyone is curious, VB.NET calls the Integer version. An interesting choice by the different language teams. – Bradley Uffner May 12 '14 at 17:57
4

This behavior is defined in the the C# Language Specification, specifically section 7.5.3 "Overload resolution." Here's a link to an older version, otherwise refer to the CSharp Language Specification.docx that you should have locally, e.g., C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1033\CSharp Language Specification.docx.

In this case, methods marked override are excluded, thus the double overload is the only valid option (emphasis mine):

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 (§7.4), and methods in a base class are not candidates if any method in a derived class is applicable (§7.6.5.1).

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • I am curious to if there would be any situations where exactly this behavior will be useful, and exploied _on purpose_? At least, the C# design team definitely had something in mind. For people familiar with other OOP languages, like Java for instance (including me) this behavior is not transparent and perhaps unfairly seen as unreasonable. – Ivaylo Slavov May 12 '14 at 18:11
  • 1
    Actually, I found the reason [here in Eric Lippert's blog](http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx). It is also present in [one of the answers](http://stackoverflow.com/questions/3674368/overload-resolution-and-virtual-methods/3677779#3677779) of a duplicate StackOverflow question. – Ivaylo Slavov May 12 '14 at 18:27
1

This behavior is apparently by design:

'When choosing an overload, if there are any compatible methods declared in a derived class, all signatures declared in the base class are ignored - even if they're overridden in the same derived class!' http://social.msdn.microsoft.com/Forums/vstudio/en-US/a70b25d4-f310-4d06-9dc2-a453f822f4f3/function-not-getting-called-when-overloading-the-function-of-base-class-in-derived-class-with?forum=csharpgeneral

edtheprogrammerguy
  • 5,957
  • 6
  • 28
  • 47