I have this class
public class Overloaded { public void ComplexOverloadResolution(params string[] something) { Console.WriteLine("Normal Winner"); } public void ComplexOverloadResolution<M>(M something) { Console.WriteLine("Confused"); } }
If I call it like this:
var blah = new Overloaded(); blah.ComplexOverloadResolution("Which wins?");
It writes Normal Winner
to the console.
But, if I add another method:
public void ComplexOverloadResolution(string something, object somethingElse = null) { Console.WriteLine("Added Later"); }
I get the following error:
The call is ambiguous between the following methods or properties: > '
Overloaded.ComplexOverloadResolution(params string[])
' and 'Overloaded.ComplexOverloadResolution<string>(string)
'
I can understand that adding a method might introduce a call ambiguity, but it's an ambiguity between the two methods that already existed (params string[])
and <string>(string)
! Clearly neither of the two methods involved in the ambiguity is the newly added method, because the first is a params, and the second is a generic.
Is this a bug? What part of the spec says that this should be the case?
This ambiguous method call error always comes with method overloading where compiler fails to find out which of the overloaded method should be used. Suppose we have a java program like below.
Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading.
Ambiguous Invocation. □ Sometimes there may be two or more possible. matches for an invocation of a method, but the. compiler cannot determine the most specific match. This is referred to as ambiguous invocation.
ambiguity error is that you named field and property tha same name "colour". change the property definition f.e. public string Colour { get { return colour;} set { colour = value;} }
Is this a bug?
Yes.
Congratulations, you have found a bug in overload resolution. The bug reproduces in C# 4 and 5; it does not reproduce in the "Roslyn" version of the semantic analyzer. I've informed the C# 5 test team, and hopefully we can get this investigated and resolved before the final release. (As always, no promises.)
A correct analysis follows. The candidates are:
0: C(params string[]) in its normal form 1: C(params string[]) in its expanded form 2: C<string>(string) 3: C(string, object)
Candidate zero is obviously inapplicable because string
is not convertible to string[]
. That leaves three.
Of the three, we must determine a unique best method. We do so by making pairwise comparisons of the three remaining candidates. There are three such pairs. All of them have identical parameter lists once we strip off the omitted optional parameters, which means that we have to go to the advanced tiebreaking round described in section 7.5.3.2 of the specification.
Which is better, 1 or 2? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 1. So 2 cannot be the winner.
Which is better, 1 or 3? The relevant tiebreaker is: a method applicable only in its expanded form is always worse than a method applicable in its normal form. Therefore 1 is worse than 3. So 1 cannot be the winner.
Which is better, 2 or 3? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 3. So 2 cannot be the winner.
To be chosen from a set of multiple applicable candidates a candidate must be (1) unbeaten, (2) beat at least one other candidate, and (3) be the unique candidate that has the first two properties. Candidate three is beaten by no other candidate, and beats at least one other candidate; it is the only candidate with this property. Therefore candidate three is the unique best candidate. It should win.
Not only is the C# 4 compiler getting it wrong, as you correctly note it is reporting a bizarre error message. That the compiler is getting the overload resolution analysis wrong is a little bit surprising. That it is getting the error message wrong is completely unsurprising; the "ambiguous method" error heuristic basically picks any two methods from the candidate set if a best method cannot be determined. It is not very good at finding the "real" ambiguity, if in fact there is one.
One might reasonably ask why that is. It is quite tricky to find two methods that are "unambigously ambiguous" because the "betterness" relation is intransitive. It is possible to come up with situations where candidate 1 is better than 2, 2 is better than 3, and 3 is better than 1. In such situations we cannot do better than picking two of them as "the ambiguous ones".
I would like to improve this heuristic for Roslyn but it is a low priority.
(Exercise to the reader: "Devise a linear-time algorithm to identify the unique best member of a set of n elements where the betterness relation is intransitive" was one of the questions I was asked the day I interviewed for this team. It's not a very hard algorithm; give it a shot.)
One of the reasons why we pushed back on adding optional arguments to C# for so long was the number of complex ambiguous situations it introduces into the overload resolution algorithm; apparently we did not get it right.
If you would like to enter a Connect issue to track it, feel free. If you just want it brought to our attention, consider it done. I'll follow up with testing next year.
Thanks for bringing this to my attention. Apologies for the error.
What part of the spec says that this should be the case?
Section 7.5.3 (overload resolution), along with sections 7.4 (member lookup) and 7.5.2 (type inference).
Note especially section 7.5.3.2 (better function member), which says in part "optional parameters with no corresponding arguments are removed from the parameter list," and "If M(p) is a non-generic method amd M(q) is a generic method, then M(p) is better than M(q)."
However, I do not understand these parts of the spec thoroughly enough to know which parts of the spec control this behavior, let alone to judge whether it is compliant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With