C# 6 中方法重载解析的重大变化 - 解释?

Breaking change in method overload resolution in C# 6 - explanation?

我们公司最近从 VS2013 迁移到 VS2017。升级后,我们的代码库将不再构建。我们会得到以下错误:

The call is ambiguous between the following methods or properties: 'IRepository<T>.Get(object, params Expression<Func<T, object>>[])' and 'IRepository<T>.Get(object, params string[])'

这是调用本身:

this.mainRepository.Get(newEntity.Id);

...和接口定义:

public interface IRepository<T> where T : class
{
    T Get(object id, params Expression<Func<T, object>>[] includeExprs);
    T Get(object id, params string[] includeExprs);
}

我想知道这里是否有人可以解释为什么会这样。我怀疑 C# 6.0 的新 改进方法重载解析 功能,但查看语言规范我无法找出导致该问题的确切规则。

编辑

我写了一篇关于这个问题的后续博客post:http://codewithstyle.info/method-overload-resolution-in-c-6-0-an-interesting-bug-story

我在升级到 Visual Studio 2015 时发现了同样的事情,所以这不是 2017 年的新功能,而是自 2013 年以来的新功能。

我在 github 上报告了这里:

Code that compiles in VS2013 fails with CS0121 in 2015; overloads with different params parameter types #4458:

问题是代码 不明确,新的 Roslyn 编译器在这方面比以前的编译器更严格。

作为问题 Add information about #4458 to "Overload Resolution.md" #4922.

的一部分,通过更改文档而不是恢复到旧行为的操作关闭了问题

特别是,AlekseyTs 评论了这个:

In the interest of the future health of our overload resolution code, we decided to keep the breaking (and correct) behavior. If we get more than this single case, we may want to reevaluate.

好了。新的编译器对此更加严格,您需要更改代码

鉴于 AlekseyTs 的上述评论,您可能 想考虑在 github 向 Microsoft 报告此问题,作为一个额外的此类案例。如果这种问题在 2017 年结束后变得更加普遍,因为很多 people/companies 都在等待升级,正如评论所说,他们可能想重新评估。

此外,您在(较旧的)文档中找不到任何相关内容的原因是,这是较旧编译器的 "hidden feature",从 change they did to the documentation 可以明显看出:

The old compiler implemented special rules for overload resolution (not in the language specification) in the presence of unused param-array parameters, and Roslyn's more strict interpretation of the specification (now fixed) prevented some programs from compiling.

(我的重点)


当我们在我们的代码中解决相同类型的问题时,我们最终得到这样的结果(使用您的代码的示例):

public interface IRepository<T> where T : class
{
    T Get(object id, Expression<Func<T, object>>[] tieBreaker, params Expression<Func<T, object>>[] includeExprs);
    T Get(object id, string tieBreaker, params string[] includeExprs);
}

注意添加了两个 tieBreaker 参数

然后我们只是将显式参数与其他参数一起包含在集合中。如果您需要能够使用这些可选额外参数的 none 调用该方法,您应该添加第三个重载,该重载没有明确说明应该调用哪个重载,因此您的最终界面可能如下所示:

public interface IRepository<T> where T : class
{
    T Get(object id);
    T Get(object id, Expression<Func<T, object>>[] tieBreaker, params Expression<Func<T, object>>[] includeExprs);
    T Get(object id, string tieBreaker, params string[] includeExprs);
}