泛型与非泛型重载调用

Posted

技术标签:

【中文标题】泛型与非泛型重载调用【英文标题】:Generic vs Non-Generic Overload Calling 【发布时间】:2012-04-12 21:53:58 【问题描述】:

当我声明这样的方法时:

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

然后用这个来调用它:

int a = 1;
DoWork(a);

它将调用什么DoWork 方法,为什么?我似乎在任何 MSDN 文档中都找不到它。

【问题讨论】:

很容易发现它会调用第二个(Visual Studio 显示大约 1 分钟),但至于为什么,我可以猜到,但对编译器有更多了解的人应该能够给你一个体面的答案。 【参考方案1】:

作为 Eric Lippert says:

The C# specification says that when you have a choice between calling ReallyDoIt&lt;string&gt;(string) and ReallyDoIt(string) – that is, when the choice is between two methods that have identical signatures, but one gets that signature via generic substitution – then we pick “自然”签名优于“替代”签名。

更新:

我们在 C# 规范 (7.5.3) 中的内容:

当在没有指定类型参数的情况下调用泛型方法时,类型推断过程会尝试推断调用的类型参数。通过类型推断,类型参数 int 由方法的参数确定。类型推断作为方法调用的绑定时处理的一部分发生,并在调用的重载解决步骤before 发生。

如果在方法调用中指定了特定方法组,并且没有将类型参数指定为方法调用的一部分,则类型推断将应用于方法组中的每个泛型方法。如果类型推断成功,则推断的类型参数用于确定后续重载决议的参数类型。如果重载决议选择一个泛型方法作为调用的方法,那么推断的类型参数将用作调用的实际类型参数。如果特定方法的类型推断失败,则该方法不参与重载决策。

所以在重载决议之前,我们在方法组中有两个方法。一个DoWork(int) 和另一个推断DoWork&lt;int&gt;(int)

然后我们转到 7.5.3.2(更好的函数成员):

如果参数类型序列 P1, P2, ..., PN 和 Q1, Q2, ..., QN 等价(即每个 Pi 有一个到相应 Qi 的恒等转换),以下平局规则应用,以便确定更好的功能成员。 1) 如果 MP 是非泛型方法,MQ 是泛型方法,则 MP 优于 MQ。

【讨论】:

在行然后我们选择“自然”签名而不是“替代”签名我们做什么意思是,它是编译器吗?为什么 natural 签名更适合这种情况?是因为性能吗? @JohnIsaiahCarmona - 这里有一些性能优势,是的,以及开发人员的一些灵活性优势。做出此选择的主要原因是规格说明如此。 平局规则在C# language spec的“7.4.3.2更好的函数成员”部分中指定:如果MP是非泛型方法,MQ是泛型方法,则MP比MQ好。 这背后的逻辑是:如果你知道如何用非泛型方法处理这种特定类型,非泛型方法获胜;否则,回退到泛型方法。 查看我关于更好的功能成员选择的评论。 不是为了性能,而是为了特异性。如果你有一个方法说“我可以 frob 长颈鹿列表”,并且你有一个方法说“我可以为你命名的任何类型 T 提供 T”,然后你说“嘿,我想 frob这个长颈鹿列表”,编译器选择专门为此而设计的方法似乎是合理的。换句话说:假设我们选择了泛型方法。 您可以编写哪些代码来选择非泛型代码?

以上是关于泛型与非泛型重载调用的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的泛型与非泛型性能

泛型集合与非泛型集合的异同

c#泛型方法重载

从非泛型类调用抽象泛型类中定义的方法的最佳方法是啥

为啥在存在非泛型时选择泛型方法?

Java集合(类)框架