通用方法重载和优先级

7

我有两个重载的泛型方法:

T Foo<T>(T t) { Console.WriteLine("T"); return t; }

T Foo<T>(int i) { Console.WriteLine("int"); return default(T); }

当我在我的电脑上尝试调用以下代码时:Foo
Foo(5);

我没有收到编译器错误或警告,而且使用泛型参数的第一个方法被调用了(即输出结果为T)。在所有C#版本和平台上都会是这种情况吗?如果是,为什么?

另一方面,如果我在泛型调用中明确指定类型:

Foo<int>(5);

调用带有int参数的第二种方法,即输出现在为int为什么?

我在我的两个方法重载中使用不同的参数名称,因此以下调用的输出结果如预期:

Foo<int>(t: 5);       // output 'T'
Foo<int>(i: 5);       // output 'int'

如果我调用第一个方法,甚至可以省略类型说明:
Foo(t: 5);            // output 'T'

但是如果我尝试编译这个代码:
Foo(i: 5);

我遇到了一个错误:方法“Foo(int)”的类型参数无法从使用中推断出。尝试显式指定类型参数。为什么编译器不能处理这个调用?

注意:这些测试是在Windows 8 x64系统上使用LinqPad执行的(如果与结果相关...)


2
请注意:这里有四个问题需要回答。 - SLaks
@SLaks 抱歉有多个问题,但我认为这些问题相关。如果需要,我可以拆分问题。而默认参数与问题无关,所以我已从代码中删除它。 - Anders Gustafsson
+1 非常好的问题。我不确定。 - SLaks
2
听起来你只需要花一点时间阅读与方法重载解析相关的规范部分。简短的答案是,这只是规范/编译器编写的功能方式... - Servy
@Servy:是的,我可能需要这样做...但在这种情况下,调用似乎反直觉。我在SO上读到(带有语言规范的参考),最专业的方法优先;如果我的第二个方法签名为 int Foo(int i),那么 这个 方法将被 Foo(5) 调用。为什么当我创建具有相同参数签名的泛型方法时,这不再成立呢? - Anders Gustafsson
1个回答

7

最后一个问题

由于您指定了(通过参数名)调用带有int参数的重载,因此编译器不知道要为T传递什么。

第一个问题

正因为如此,Foo(5) 只匹配一个重载Foo<T>())。
因此,它必须仅调用Foo<T>()

第二个问题

当您明确指定类型参数(<int>)时,两个重载都适用。
在这种情况下,Foo(int)更好,因为其参数不是泛型类型。

根据C#规范§7.5.3.2:

  • 否则,如果MP具有比MQ更具体的参数类型,则MP优于MQ。让{R1,R2,…,RN}和{S1,S2,…,SN}表示MP和MQ的未实例化和未扩展参数类型。如果对于每个参数,RX不比SX不具体,并且对于至少一个参数,RX比SX更具体,则MP的参数类型比MQ的参数类型更具体:
    • 类型参数不比非类型参数具体。

(强调添加)


@AndersGustafsson: 我回答了另外两个问题。 - SLaks
很棒的解释,@SLaks!现在一切都清楚了 :-) 非常感谢! - Anders Gustafsson
1
@AndersGustafsson:关键点(我在规范中找不到明确说明)是具有无法推断类型参数的方法不适用于重载决策。如果这不是真的,那么Foo(5)将会给出相同的“无法推断参数”错误,因为Foo <?>(int)将成为更好的选择。 - SLaks
1
@SLaks:好答案。你要找的规范行是“如果F是泛型且M没有类型参数列表,则当类型推断成功时,F是一个候选项...”在7.6.5.1节中。 - Eric Lippert
@EricLippert:谢谢。我知道肯定有这样一行代码,只是找不到它。 - SLaks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接