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

我有课

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


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

如果我这样说:

        var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");

它将 Normal Winner写入控制台。

但是,如果我加上另一种方法:

    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)'

我可以理解,添加一个方法可能会引入一个调用模糊性,但它是两个已经存在的 (params string[])<string>(string)方法之间的一个模糊性!显然,这种模糊性涉及的两个方法都不是新添加的方法,因为第一个是参数,第二个是泛型。

Is this a bug? What part of the spec says that this should be the case?

3184 次浏览

如果从第一个方法中删除 params,就不会发生这种情况。您的第一个和第三个方法都有有效的调用 ComplexOverloadResolution(string),但是如果您的第一个方法是 public void ComplexOverloadResolution(string[] something),则不会有歧义。

为参数 object somethingElse = null提供值使其成为可选参数,因此在调用该重载时不必指定它。

编辑: 编译器在这里做了一些疯狂的事情。如果在第一个方法之后移动代码中的第三个方法,它将正确地报告。所以它似乎采取了前两个超载,并报告他们,没有检查正确的一个。

‘ Console Application1.Program. ComplexOverloadResolment (params string [])’和 ‘ Console Application1.Program. ComplexOverloadResolment (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.

What part of the spec says that this should be the case?

第7.5.3节(重载解析) ,以及第7.4节(成员查找)和7.5.2节(类型推断)。

特别要注意的是第7.5.3.2节(更好的函数成员) ,其中部分内容是“没有相应参数的可选参数从参数列表中删除”,以及“如果 M (p)是一个非泛型方法,而 M (q)是一个泛型方法,那么 M (p)比 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.

  1. 如果你写

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    或者只是写作

    var blah = new Overloaded();
    blah.ComplexOverloadResolution();
    

    在方法上,它将以 用同样的方法结束

    public void ComplexOverloadResolution(params string[] something
    

    这是导致 params关键字,使得它最好的匹配时也为情况下的 没有指定参数

  2. 如果您尝试像下面这样添加新方法

    public void ComplexOverloadResolution(string something)
    {
    Console.WriteLine("Added Later");
    }
    

    它将完美地编译并调用这个方法,因为它是一个带有 string参数的 完美匹配调用。比 params string[] something强多了。

  3. 像这样声明第二个方法

    public void ComplexOverloadResolution(string something, object something=null);
    

    编译器,在第一个方法和这个方法之间完全混乱,刚刚添加了一个。 因为它不知道他现在应该在你的电话上所有的功能

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");
    

    事实上,如果从调用中删除字符串参数,就像下面的代码一样,那么所有代码都会正确编译,并且工作方式与之前的 一样

    var blah = new Overloaded();
    blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.
    

这是窃听器吗?

是的。

祝贺您,您已经在超载分辨率中发现了一个 bug。该错误在 C # 4和 C # 5中重现; 在语义分析器的“ Roslyn”版本中不重现。我已经通知了 C # 5测试团队,希望我们能够在最终版本发布之前调查并解决这个问题。(一如既往,没有承诺。)

下面是正确的分析,候选人是:

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string)
3: C(string, object)

候选零显然是不适用的,因为 string不能转换为 string[]

在这三种方法中,我们必须确定一种独特的最佳方法。我们通过对剩下的三个候选人进行配对比较来做到这一点。有三对这样的。一旦我们去掉省略的可选参数,它们都有 一模一样参数列表,这意味着我们必须进入规范的7.5.3.2部分中描述的高级抢七回合。

哪个更好,一个还是两个?相关的决胜局是泛型方法总是比非泛型方法更糟糕。2比1更糟。所以2号不可能是赢家。

哪个更好,一个还是三个?相关的决胜局是: 只适用于其扩展形式的方法总是比适用于其规范形式的方法差。所以1比3差。所以1不可能是赢家。

哪个更好,两个还是三个?相关的决胜局是泛型方法总是比非泛型方法更糟糕。2比3更糟。所以2号不可能是赢家。

要从一组多个适用的候选人中选择一个候选人必须(1)不败,(2)击败至少一个其他候选人,和(3)是唯一的候选人,具有前两个属性。候选人三是没有其他候选人击败,并击败至少一个其他候选人,它是唯一的候选人与此财产。因此候选三是 独一无二的最佳候选人。它应该会赢。

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.

人们可能会合理地问为什么会这样。很难找到“明确不明确”的 方法,因为“ better”关系是 不及物动词。有可能出现候选者1优于2,2优于3,3优于1的情况。在这种情况下,我们最好选择其中两个作为“模棱两可的人”。

我希望为罗斯林改进这种启发式方法,但它的优先级较低。

(给读者的练习: “设计一个线性时间算法来确定一组 n 元素中最好的元素,这些元素的优劣关系是不及物的”是我为这个团队面试那天被问到的问题之一。这不是一个很难的算法,试试看吧。)

我们之所以这么长时间推迟在 C # 中添加可选参数,原因之一是它在重载解析算法中引入了大量复杂的模糊情况; 显然我们没有做对。

如果您想输入一个 Connect 问题来跟踪它,请随意。如果你只是想引起我们的注意,那就这么定了。明年我会继续进行测试。

谢谢你告诉我这个,很抱歉出了错。

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

像这样:

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");
}
}