为什么要添加一个方法添加一个模糊的调用,如果它不会涉及到模糊性

我有这个class

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

我得到以下错误:

在以下方法或属性之间的调用是不明确的:> Overloaded.ComplexOverloadResolution(params string[]) 'and Overloaded.ComplexOverloadResolution<string>(string) '

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

这是一个错误? 规范的哪一部分说这应该是这种情况?

这是一个错误?

是。

恭喜,你已经发现了一个重载分辨率的错误。 在C#4和5中重现错误; 它不会在“Roslyn”版本的语义分析器中重现。 我已经通知了C#5testing团队,希望我们能在最终发布之前得到调查和解决。 (一如既往,没有承诺。)

一个正确的分析如下。 候选人是:

 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中描述的高级决斗轮。

哪个更好,1或2? 相关的tiebreaker是generics方法总是比非generics方法更糟糕。 2比1差。所以2不能成为赢家。

哪个更好,1或3? 相关的决胜因素是:一种只适用于其扩展forms的方法总是比正常forms适用的方法更差。 所以1比3差。所以1不能成为赢家。

哪个更好,2或3? 相关的tiebreaker是generics方法总是比非generics方法更糟糕。 2比3差。所以2不能成为赢家。

从一组多个适用的候选人中select一个候选人必须(1)不败,(2)击败至less一个其他候选人,(3)是具有前两个属性的独特候选人。 候选人三被其他候选人殴打,并击败至less一名其他候选人; 它是这个属性的唯一候选人。 所以候选人三是唯一的最佳人选 。 它应该赢。

C#4编译器不仅错误,因为您正确地注意到它报告了一个奇怪的错误消息。 编译器得到重载分辨率分析错误是有点令人惊讶。 这是错误的错误信息是完全不足为奇; 如果不能确定最佳方法,“模糊方法”错误启发式方法基本上从候选集合中挑选出两种方法。 如果事实上有一个,那么find“真正的”模糊不是很好。

有人可能会问这是为什么。 find两个 “毫不含糊”的方法是相当棘手的,因为“更好”关系是不灵敏的。 有可能提出候选人1比2好,2比3好,3比1更好的情况。在这种情况下,我们不能比把他们中的两个当作“模棱两可的人”来做得更好。

我想改善Roslyn的这种启发式,但它是一个低优先级。

(对读者进行练习:“devise一个线性时间algorithm来识别n个元素中最好的成员,这个元素的优化关系是不及物动词的”是我在这个团队采访的那一天被问到的问题之一。一个非常困难的algorithm;给它一个镜头。)

我们推迟向C#添加可选参数的原因之一就是它引入重载parsingalgorithm的复杂含糊情况的数量; 显然我们没有得到正确的答案。

如果您想要input连接问题进行跟踪,请随时取消。 如果你只是想让它引起我们的注意,那就考虑一下。 明年我会跟进testing。

感谢您将此引起我的注意。 抱歉的错误。

规范的哪一部分说这应该是这种情况?

第7.5.3节(重载决议),连同第7.4节(成员查询)和第7.5.2节(types推断)。

特别注意7.5.3.2节(更好的函数成员),其中部分说明“没有相应参数的可选参数将从参数列表中删除”,以及“如果M(p)是非generics方法,则M(q)一个通用的方法,那么M(p)比M(q)好。“

但是,我不太了解规范的这些部分,以便知道规范的哪些部分可以控制这种行为,更不用说判断它是否符合规范。

您可以通过更改某些方法中第一个参数的名称并指定要分配的参数来避免这种歧义

喜欢这个 :

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

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

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

编辑:编译器在这里做一些疯狂的东西。 如果你在第一个代码之后移动你的第三个方法,它会正确地报告。 所以它似乎是采取了前两个重载和报告,没有检查正确的。

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

编辑2:新的发现。 从上述三个方面中删除任何方法将不会产生两者之间的矛盾。 所以看来冲突只会出现,如果有三种方法存在,无论顺序。

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

    编译器,第一种方法和这个之间的混淆,只是加了一个。 因为它不知道他现在应该在你的通话中应该使用哪个function

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

    事实上,如果你从调用中删除string参数,就像下面的代码一样,一切编译正确,像以前一样工作

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