Thursday, January 19, 2006 6:11 PM
nealho
Overrides and Overload Resolution
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.