通用 vs 非通用重载调用

8

当我像这样声明一个方法:

void DoWork<T>(T a) { }
void DoWork(int a) { }

并使用以下代码调用:

int a = 1;
DoWork(a);

它会调用哪个DoWork方法,为什么?我似乎在MSDN文档中找不到它。
它会调用哪个DoWork方法以及为什么呢?我在MSDN文档中找不到。

很容易发现它会调用第二个函数(在Visual Studio中大约需要1分钟),但是为什么会这样,我可以猜测,但是对编译器有更多了解的人应该能够给你一个合理的答案。 - Richard Dalton
1个回答

9

正如Eric Lippert所的:

C#规范指出,当你在调用ReallyDoIt<string>(string)ReallyDoIt(string)之间做出选择时——也就是说,当两个方法具有相同的签名,但一个通过泛型替换得到该签名时——我们会选择“自然”签名而不是“替代”签名。

更新:

在C#规范(7.5.3)中所述:

当调用泛型方法而没有指定类型参数时,类型推断过程会尝试为调用推断类型参数。通过类型推断,从方法的参数中确定类型参数int。类型推断是方法调用的绑定时间处理的一部分,在调用的重载解析步骤之前进行。

当在方法调用中指定特定的方法组时,并且作为方法调用的一部分未指定类型参数,则将类型推断应用于方法组中的每个泛型方法。如果类型推断成功,则使用推断出的类型参数来确定后续重载决策的参数类型。如果重载决策选择泛型方法作为要调用的方法,则推断出的类型参数将用作调用的实际类型参数。如果特定方法的类型推断失败,则该方法不参与重载决策。
因此,在重载决策之前,我们有两种方法,一种是DoWork(int),另一种是推断出的DoWork<int>(int)
然后我们进入7.5.3.2(更好的函数成员):
如果参数类型序列{P1,P2,…,PN}和{Q1,Q2,…,QN}等效(即每个Pi都具有对应Qi的标识转换),则按顺序应用以下打破平局规则以确定更好的函数成员。1)如果MP是非泛型方法且MQ是泛型方法,则MP优于MQ。

在“然后我们选择‘自然’签名而不是‘替代’签名”的这一行中,我们指的是编译器吗?为什么在这种情况下“自然”签名更可取?是因为性能吗? - John Isaiah Carmona
@JohnIsaiahCarmona - 这里确实有一些性能优势,以及对开发人员来说的一些灵活性优势。选择这种方式的主要原因是规格要求如此。 - Polity
1
挑选胜者的规则在C# 语言规范的“7.4.3.2 更好的函数成员”一章中有明确说明:如果MP是非泛型方法并且MQ是泛型方法,则MP优于MQ。 这个规则的逻辑是:如果你知道如何用非泛型方法处理这个特定类型,那么非泛型方法胜出;否则,就退而求其次选择泛型方法。 - Jiaji Wu
请查看我的有关更好的函数成员选择的评论。 - Sergey Berezovskiy
5
这不是为了性能,而是为了特定性。如果你有一个方法说“我可以 frob 一个长颈鹿列表”,还有一个方法说“我可以 frob 任何你指定的类型 T 的 T”,然后你说“嘿,我想 frob 这个长颈鹿列表”,编译器选择专门用来执行这项任务的方法似乎是合理的。换句话说:假设我们选了通用的那个方法。你可能写什么代码才会选择非通用的那个方法呢? - Eric Lippert

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