假设有这样一个类。
public class SomeClass<T>
{
public void DoSomething(int x)
{
Console.WriteLine("DoSomething(int x) called");
}
public void DoSomething(T x)
{
Console.WriteLine("DoSomething(T x) called");
}
}
还有一段代码
var someObj = new SomeClass<int>();
我有两个问题。
1) 私有类型SomeClass<int>有两个具有相同签名的方法。那为什么编译器不骂他们呢?
2)说如果打电话
someObj.DoSomething(0);
这段代码写着“DoSomething(int x) called”。为什么使用整数参数而不是通用参数调用变体?
规范摘录(条款 7.5.3.2):
在俄语中听起来像这样:
还有更多的 dropout 规则,但对于这种情况,其余规则不是必需的。
好的,从发现腿长出来的地方,规范中详细说明了这种行为。它在实施中是什么样子的?让我们看一下编译后的代码并比较泛型和非泛型方法。我使用 ILSpy,但您可以使用任何熟悉的工具,如 ILDasm。
如您所见,泛型类方法的代码在编译后仍然是泛型的(注意
!第二个方法中类型名称的前缀)。当被问及为什么选择以这种方式出现,而不是相反时,他们回答说。现在稍微介绍一下这种实施的可能原因和选择规则。可能,因为我不是这个规范的开发者,但是从一般逻辑可以得出一些结论。
在泛型方法中,您只能将语言特性应用于泛型类型变量。例如,除相等和不相等检查外,不能使用比较运算符,因为 我们不能保证它们是在调用泛型方法的类型中实现的。因此,如果有合适的非通用方法可用,则很可能通过使用特定类型的专门工具来更有效地实现它。
在调用泛型方法之前,编译器必须用真实类型替换泛型类型。使用非泛型方法时不需要这些操作,这意味着即使在微不足道的情况下,当方法的效率相同时,调用泛型方法的成本微不足道,但更高。
事实证明,泛型方法会损失一点性能,但有什么回报呢?毕竟,我们有一个共同的根类型
object,任何其他类型也可以归结为该类型。我是这样看的:当使用泛型方法时,不使用类型转换,就像使用 的情况一样
object,这是一个速度增益,特别是对于 ValueType 类型,因为 没有拳击。使用泛型类型时,可以确定两个变量是否具有相同的泛型类型。那么它们的类型在执行期间也将是相同的,不像 type 的变量
object,它可以包含任何东西,并且需要额外的检查来消除运行时的错误。在使用泛型类型时,我们可以随心所欲地使用细化或限制,并显式指定:它是一个类还是结构,是否存在构造函数(目前只有默认构造函数),基类型,以及一个列表必须在类型中实现的接口,允许在特定的泛型方法或类中使用。与前一个选项一样,此选项
object只能通过额外检查和增加所需代码量来实现。自然地,对于这个特定实现的真正原因,我可能是错误的。当然,优点和缺点的清单远非完整。但我认为这足以让您大致了解为什么泛型类型和与之相关的一切都是它们现在的样子,以及如何将它们用于预期目的并永久使用。
PS:除了引用规范和IL代码之外的所有内容都是我个人的意见,如果你不同意,那么我们可以在聊天中讨论,或者你可以写下你自己对这个问题的回答并表达你的观点。
特别感谢@Vadim Ovchinnikov对之前版本的批评。