Why would adding a method add an ambiguous call, if it wouldn't be involved in the ambiguity

Solution 1:

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.

Solution 2:

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.

Solution 3:

you can avoid this ambiguity by changing the name of the first parameter in some methods and specifying the parameter which you want to assign

like this :

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] somethings)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Overloaded a = new Overloaded();
        a.ComplexOverloadResolution(something:"asd");
    }
}

Solution 4:

If you remove the params from your first method, this wouldn't happen. You first and third method have both valid calls ComplexOverloadResolution(string), but if your first method is public void ComplexOverloadResolution(string[] something) there will be no ambiguity.

Supplying value for a parameter object somethingElse = null makes it optional parameter and thus it doesn't have to be specified when calling that overload.

Edit: The compiler is doing some crazy stuff here. If you move your third method in code after your first, it will report correctly. So it seems it's taking the first two overloads and report them, without checking for the correct one.

'ConsoleApplication1.Program.ComplexOverloadResolution(params string[])' and 'ConsoleApplication1.Program.ComplexOverloadResolution(string, object)'

Edit2: New finding. Removing any method from the above three will produce no ambiguaty between the two. So it seems the conflict only appears if there are three methods present, regardless of order.