I got an e-mail earlier this week asking a fairly common question regarding C#’s overload resolution.  Before I launch into a discussion of the actual behavior of the compiler, think about what the output of the following program will be:

using System;

public class Program {
    public static void Main() {
        Derived d = new Derived();
        Console.WriteLine(d.Method("My String"));
    }
}

public class Base {
    public virtual string Method(string s) {
        return "Base.Method(string)";
    }
}

public class Derived : Base {
    public override string Method(string s) {
        return "Derived.Method(string)";
    }
    public virtual string Method(object o) {
        return "Derived.Method(object)";
    }
}

My first thought, as well as the first thought of most people I’ve talked to about this, is that this code would call Derived.Method(string) because it is the best overload.  However, as you either know from having tried it or have already guessed from the very fact that I’m writing this post, the method that actually gets called is Derived.Method(object).  In tracking this issue down, I started where I usually do—with the C# language spec.  As I skimmed through overload resolution, it seemed to confirm what I had thought originally—a method that requires no conversion is a better choice than a method that requires a conversion.  Puzzled, I moved on, and read the part about member access, where I concluded that the result of looking up Method on an instance of Derived should be the method group containing both overloads.  Well, that was no help, so I read deeper.  Looking at the next section, about invocation expressions, I read the following:  “The set of candidate methods is reduced to contain only methods from the most derived types.”  Some normative text followed, but this is the detail I had been missing.

The methods in a derived class are preferred to inherited methods.  Add to that the fact that the compiler doesn’t consider overrides when performing overload resolution—only methods introduced by a given class are considered to be on that class—and, surprisingly enough, the compiler is making the correct choice.