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.[2. 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!