params超载明显模糊 – 仍然编译和工作?

我们在代码中找到了这些:

public static class ObjectContextExtensions { public static T Find(this ObjectSet set, int id, params Expression<Func>[] includes) where T : class { ... } public static T Find(this ObjectSet set, int id, params string[] includes) where T : class { ... } } 

如您所见,除了params之外,它们具有相同的特征。

它们被用于多种方式,其中之一:

 DBContext.Users.Find(userid.Value); //userid being an int? (Nullable) 

对我来说奇怪的是,它解决了第一次超载。

Q1:为什么这不会产生编译错误?

Q2:为什么C#编译器解析了上面第一个方法的调用?

编辑 :只是为了澄清,这是C#4.0,.Net 4.0,Visual Studio 2010。

这显然是重载解析中的一个错误。

它在C#5和C#3中再现,但在Roslyn中没有; 我不记得我们是否决定故意改变或者这是一次意外。 (我现在的机器上没有C#4,但如果它在3和5中重新编号,那么它几乎可以肯定在4中。)

我把它引起了罗斯林团队前同事的注意。 如果他们回复我的任何有趣的东西,我会更新这个答案。

由于我无法访问C#3/4/5源代码,因此我无法说明错误的原因。 考虑在connect.microsoft.com上报告它。

这是一个非常简化的复制品:

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

它似乎与元素类型的通用性有关。 奇怪的是,克里斯在答案中指出,编译器选择更通用的一个! 我本来期望这个bug是另一种方式,并选择不那么通用的bug。

顺便提一下,这个错误可能是我的错,因为我在C#3中对重载解析算法做了相当多的工作。为错误道歉。

UPDATE

我在罗斯林队的间谍告诉我,这是一个长期处于超载分辨率的已知错误。 实施了一个决胜局规则,从未记录或certificate,具有更大通用arity的类型是更好的类型。 这是一个奇怪的规则,没有任何理由,但它从未被从产品中删除。 Roslyn团队前段时间决定采取重大改变并修复重载决议,以便在这种情况下产生错误。 (我不记得那个决定,但我们做了很多关于这类事情的决定!)

在IDEONE上,编译器成功生成错误 。 如果您逐步分析分辨率算法,那应该是一个错误:

1)构造方法调用的候选方法集。 从与前面的成员查找找到的M相关联的方法开始[…]集合缩减包括将以下规则应用于集合中的每个方法TN,其中T是方法N中的类型声明:

为简单起见,我们可以在此推断出这里的方法集包含了两种方法。

然后减少进行:

2)如果N不适用于A( 第7.4.2.1节 ),则从集合中移除N.

这两种方法都适用于扩展forms的适用function成员规则:

通过使用参数数组的元素类型的零个或多个值参数替换函数成员声明中的参数数组来构造扩展forms,使得参数列表A中的参数数量与参数的总数相匹配。 如果A的参数少于函数成员声明中固定参数的数量,则无法构造函数成员的展开forms,因此不适用。

此规则在缩减集中保留两种方法。

实验(在一个或两个方法中将id参数类型更改为float )确认两个函数都保留在候选集中,并且通过隐式转换比较规则进一步区分。

这表明上述算法在创建候选集方面工作正常,并且不依赖于某些内部方法排序。 由于唯一可以进一步区分方法的是重载解析规则, 这似乎是一个错误 ,因为:

最佳函数成员是一个函数成员,它比给定参数列表中的所有其他函数成员更好,前提是使用第7.4.2.2节中的规则将每个函数成员与所有其他函数成员进行比较。

并且显然这些方法都没有比其他方法更好,因为这里不存在隐式转换。

这不是一个完整的答案,因为它解释了差异,但不是为什么。 它确实需要完整性的规范参考。 但是我不想让我所做的研究在评论中迷失,所以我发布了答案。

两个重载之间的区别在于,一个的参数是通用的,而另一个不是。 编译器似乎认为generics类型比非generics更接近。

也就是说,如果将Expression<...>类型更改为int则编译器会抱怨模糊性。 类似如果类型都是通用的那么它就会抱怨模糊性。

以下代码段将更简单地展示此行为:

 void Main() { TestMethod(); } public void TestMethod(params string[] args) { Console.WriteLine("NonGeneric"); } public void TestMethod(params List[] args) { Console.WriteLine("Generic"); } 

这将打印“Generic”。