params overload apparent ambiguity - still compiles and works?

c#, overloading, parameters, roslyn

Solution

This is clearly a bug in overload resolution.

It reproduces in C# 5 and C# 3 but not in Roslyn; I do not recall if we decided to deliberately take the breaking change or if this is an accident. (I don't have C# 4 on my machine right now but if it repros in 3 and 5 then it will in 4 also almost certainly.)

I have brought it to the attention of my former colleagues on the Roslyn team. If they get back to me with anything interesting I'll update this answer.

As I no longer have access to the C# 3 / 4 / 5 source code I am unable to say what the cause of the bug is. Consider reporting it on connect.microsoft.com.

Here's a much-simplified repro:

class P
{
    static void M(params System.Collections.Generic.List<string>[] p) {}
    static void M(params int[] p)  {}
    static void Main()
    {
        M();
    }
}

It appears to have something to do with the genericity of the element type. Bizarrely, as Chris points out in his answer, the compiler chooses the more generic one! I would have expected the bug to be the other way, and choose the less generic one.

The bug is, incidentally, likely my fault, as I did a fair amount of the work on the overload resolution algorithm in C# 3. Apologies for the error.

UPDATE

My spies in the Roslyn team tell me that this is a known bug of long standing in overload resolution. There was a tiebreaker rule implemented that was never documented or justified that said that the type with the bigger generic arity was the better type. This is a bizarre rule with no justification, but it's never been removed from the product. The Roslyn team decided some time ago to take the breaking change and fix overload resolution so that it produces an error in this case. (I don't recall that decision, but we made a lot of decisions about this sort of thing!)

Problem

We just found these in our code: ``` public static class ObjectContextExtensions { public static T Find<T>(this ObjectSet<T> set, int id, params Expression<Func<T, object>>[] includes) where T : class { ... } public static T Find<T>(this ObjectSet<T> set, int id, params string[] includes) where T : class { ... } } ``` As you can see, these have the same signature except for the `params`. And they're being used in several ways, one of them: ``` DBContext.Users.Find(userid.Value); //userid being an int? (Nullable<int>) ``` which, strangely enough to me, resolves to the first overload. Q1: Why doesn't this produce a compile error? Q2: Why does the C# compiler resolve the above call to the first method? Edit: Just to clarify, this is C# 4.0, .Net 4.0, Visual Studio 2010.

Original source

Related problems