Changes on overloaded method behaviour between visual studio 2013 and 2015

216 Views Asked by At

Have have big problem with Visual Studio 2013 and 2015. In one class, i have defined this two methods:

  1. public List<T> LoadData<T>(string connectionStringName = "", string optWherePart = "", params object[] parameter)
  2. public List<T> LoadData<T>(string optWherePart, params object[] parameter)

I only want to call the second method like this:

....LoadData<Config_Info>("ConfigName LIKE 'Version' AND UserName LIKE '' AND PlugInName Like ?", parameter: ProductName);

If i go to definition in Visual Studio 2013, i come to the second method declaration, but in Visual Studio 2015, i come to the first. Both solutions are absolutly identically.

Even the compiled result is different, so if i compile the same solution with VS 2015, the program stops working.

This is a very strange behaviour.

Does any one has an idea, what the differences are?

1

There are 1 best solutions below

1
On BEST ANSWER

This is based on the C# 5 specification, but since the C# 6 specification doesn't appear to have been published yet that's the best I can do. It's also an attempt to invoke Cunningham's Law.


As a preliminary, in the language of s7.5.3.1 ("Applicable Function Member") of the spec, both function members are applicable (either of them could be called if the other didn't exist) in their expanded form (the params object[] can't be fulfilled by the string ProductName so is converted to an object argument).

Thus we move on to s7.5.3.2 ("Better Function Member") in order to decide which of the two is the better function to call.


Firstly, a stripped-down argument list A is constructed containing just the argument expressions themselves in the order they appear in the original argument list:

  • { string "ConfigName [...]", string ProductName }

Next, [p]arameter lists for each of the candidate function members are constructed in the following way:

  • The expanded form is used if the function member was applicable only in the expanded form.
  • Optional parameters with no corresponding arguments are removed from the parameter list
  • The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list.

This gives us the following:

  • { string connectionStringName, object parameter } (optWherePart removed, params expanded)
  • { string optWherePart, object parameter } (params expanded)

We then have a sequence of comparisons to make to decide which of these is the better function member. Calling one Mp and one Mq, these go as follows:

  • If Mp is a non-generic method and Mq is a generic method, then Mp is better than Mq.
    • No difference here
  • Otherwise, if Mp is applicable in its normal form and Mq has a params array and is applicable only in its expanded form, then Mp is better than Mq.
    • No difference here; both are in their expanded form
  • Otherwise, if Mp has more declared parameters than Mq, then Mp is better than Mq. This can occur if both methods have params arrays and are applicable only in their expanded forms.
    • Not 100% on this one. Both our argument lists use 2 of the parameters from the original function definitions. I think this is solely meant to distinguish between one case where both arguments went into the same params array and one where one went into the array and one went into a normal argument.
  • Otherwise if all parameters of Mp have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in Mq then Mp is better than Mq.
    • Aha! Our first argument list is missing optWherePart, which needed a default argument, so the second argument list is better! So VS2015 is wrong!

... but wait. What does this last bullet even mean? Mp and Mq are specifically parameter lists where [o]ptional parameters with no corresponding arguments are removed. There is no way either of them could not have a corresponding argument because if they didn't, they'd have been removed.


In conclusion, I can't tell whether this is a bug in the old compiler, the new compiler... or the C# specification.

I've found a blog post by SLaks that also seems to think the old behaviour was a bug. The blog states that Roslyn had fixed this by making the compiler fail, and that's not what I see any more. Maybe they changed their minds?


Edit: Update! My Roslyn bug report resulted in a change to the compiler to ensure that, in this case, the second overload is picked. This appears to be because of the default arguments need to be substituted wording above. I still think the spec is ambiguous, so I'm disappointed that only a code change was made (and not a spec change, or even a discussion about why the second overload is the better one), but at least the VS2015 runtime beaviour is now the same as it was in VS2013.