Overload resolution is of course the process of taking a bunch of things with the same name and figuring out which of them the user meant. Different languages use different heuristics to try to figure this out. A “heuristic” is just a fancy word for a guess, and I’ve often said that one of the design characteristics of C# is that it is not a “guess what the user meant” kind of language. So if C# is going to make guesses, at least the process by which it does so should be easily explainable to users.
The aspect of overload resolution in C# I want to talk about today is really the fundamental rule by which one potential overload is judged to be better than another for a given call site: closer is always better than farther away. There are a number of ways to characterize “closeness” in C#. Let’s start with the closest and move our way out:
- 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.
The overload resolution algorithm can be thought of as making a list of every method of a given name, ordering them by closeness using the above metrics, discarding all methods that are farther away than the closest method, and then doing additional tiebreakers on the methods that remain. Of course that is not actually what the compiler does; if for example any instance method can be found then searching for extension methods is elided.
Once you realize that this characterization of “closeness” is the first and deepest cut that the overload resolution algorithm makes, answers to other questions become clear:
- Why does overload resolution choose an inexact match declared in a derived class over an exact match in a base class? Because the method in the derived class is closer.
- If I have two similarly-named extension methods, how can I force one to be automatically better than the other? Put the using directive that brings the extension method’s class into action closer to the call site.
And with that, I’m done for 2013. This was an interesting and eventful year for me. New job, new blog location, new office, new product. I’m looking forward to things being a little more routine in 2014. Thanks all for your always-interesting comments and questions. See you for more fabulous adventures next year!
Missing: a match.com analogy.
Hi Eric,
Great post! Found it in your SO answer on the same subject (http://stackoverflow.com/a/1451184). I noticed one of the comments in that post asked about hiding a class method with an extension method, but you explained that class hierarchy always wins over an extension method.
But what about hiding a class method with another overload with default parameters? I know OR heuristics specify that “preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call.” Do you know why the Obsolete attribute doesn’t seem to affect the OR decision on judging two candidates to be equally good?
For example, it seems OR could judge the overload with an optional parameter to be better given the following markup on the default overload:
[Obsolete(“This method is deprecated.”)]
[EditorBrowsable(EditorBrowsableState.Never)]]
bool foo() { return true; }
bool foo(bool optional = false) { return optional; }
I would expect false to be returned from a call to foo() above. Or do you know any way to make that work? Thanks for your insight,
-Mike
Pingback: 如何覆盖现有的扩展方法 – CodingBlog
Pingback: C# method override resolution weirdness | Developer FAQs
Pingback: Почему вызывается перегруженный метод? - c# c#-faq - Вопросы и ответы по программированию
Pingback: How to override an existing extension method – Row Coding