This issue has caught me out once again. Could someone provide a technical explanation as to why the following code does not produce any warnings or errors. The question you have to ask yourself is (of course) do you feel lucky?
class Program
{
static string Feeling(object o) { return "Lucky"; }
static string Feeling(string s) { return "Unlucky"; }
static void Main(string[] args)
{
Console.WriteLine("I feel " + Feeling(null));
}
}
Bonus points awarded if you know which method will be called without running the code.
And just to add insult, it doesn’t just happen with null parameters:
class Program
{
static string Feeling(int i) { return "Lucky"; }
static string Feeling(uint i) { return "Unlucky"; }
static void Main(string[] args)
{
Console.WriteLine("I feel " + Feeling(7));
}
}
First example: null argument
In the first case it will call the
stringoverload.nullmatches bothobjectandstring, but string is the more specific/derived type. Thus it choosesstring.Check Eric Lippert’s post How does the method overload resolution system decide which method to call when a null value is passed? for a longer explanation for this part of overload resolution.
Second example: integer literal
In the second case it’ll choose the first overload, because the literal
7isint. If you had used7uit would have beenuintand thus the second overload would be preferred.Integer literals have a well defined type(even if they allow more implicit conversions than normal integral values). You can use suffixes like
ufor unsigned, orlfor long to influence that type. Or you can add an explicit cast.While normally an
intwouldn’t be implicitly convertible touint, this is an integer constant which is in the valid range ofuint, and the C# compiler has an extra rule to allow implicit conversions between integer constants, provided the constant fits the target’s range.One again Eric explains the details: Why does this implicit conversion from int to uint work?
In both examples one overload is clearly the best, as far as the C# compiler is concerned, and thus you don’t get an ambiguous overloading error.
Personally I think that the first example should give a warning, but either the C# team disagrees, or they simply didn’t have time to add that heuristic.