为什么编译器发出框指令来比较引用types的实例?

下面是一个简单的genericstypes,它具有一个唯一的通用参数约束引用types:

class A<T> where T : class { public bool F(T r1, T r2) { return r1 == r2; } } 

由csc.exe生成的IL是:

 ldarg.1 box !T ldarg.2 box !T ceq 

因此,在进行比较之前,每个参数都是装箱的。

但是,如果约束指示“T”不应该是一个值types, 为什么编译器试图将r1r2

需要满足生成的IL的可validation性约束。 请注意, 无法validation不一定意味着不正确 。 只要它的安全上下文允许运行不可validation的代码,它就可以正常工作。 validation是保守的,并基于一个固定的规则集(如可达性 )。 为了简化事情,他们select不关心validationalgorithm中genericstypes约束的存在。

通用语言基础结构规范(ECMA-335)

第9.11节:通用参数的约束

…generics参数的约束只限制generics参数可能被实例化的types。 validation(请参阅分区III)要求通过满足约束已知通用参数的字段,属性或方法不能通过通用参数直接访问/调用, 除非首先装箱 (请参阅分区III)或callvirt指令以受constrained前缀指令作为前缀。 …

删除box指令将导致无法validation的代码:

 .method public hidebysig instance bool F(!T r1, !T r2) cil managed { ldarg.1 ldarg.2 ceq ret } c:\Users\Mehrdad\Scratch>peverify sc.dll Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.1 Copyright (c) Microsoft Corporation. All rights reserved. [IL]: Error: [c:\Users\Mehrdad\Scratch\sc.dll : A`1[T]::F][offset 0x00000002][fo und (unboxed) 'T'] Non-compatible types on the stack. 1 Error(s) Verifying sc.dll 

更新(回复评论):正如我上面提到的,可validation性不等同于正确性(这里我从types安全angular度谈论“正确性”)。 可validation程序是正确程序的严格子集(即所有可validation的程序都certificate是正确的,但是有正确的程序是不可validation的)。 因此,可证实性比正确性更强。 由于C#是一种图灵完备语言, Rice的定理指出certificate程序是正确的,在一般情况下是不可判定的。

让我们回到我的可达性比喻,因为它更容易解释。 假设你正在deviseC#。 有一件事情想到的是什么时候发出关于无法访问的代码的警告,并在优化器中完全删除这段代码,但是如何检测所有无法访问的代码呢? 赖斯的定理再次表明,你不能为所有的节目做到这一点。 例如:

 void Method() { while (true) { } DoSomething(); // unreachable code } 

这是C#编译器实际上警告的事情。 但它并没有警告:

 bool Condition() { return true; } void Method() { while (Condition()) { } DoSomething(); // no longer considered unreachable by the C# compiler } 

一个人可以certificate在后一种情况下,控制stream程永远不会到达那条线。 有人可能会争辩说,在这种情况下,编译器可以静态certificateDoSomething是无法访问的,但事实并非如此。 为什么? 关键是你不能为所有的程序,所以你应该画线。 在这个阶段,你必须定义一个可判断的属性并称之为“可达性”。 例如,为了达到可达性,C#坚持常量expression式,并且根本不会查看函数的内容。 简单的分析和devise一致性是决定在哪里画线的重要目标。

回到我们的validation概念,这是一个类似的问题。 可validation性与正确性不同,是可判定的财产。 作为运行时devise者,您必须根据性能考虑,易于实现,易于规范和一致性来决定如何定义可validation性,从而使编译器可以轻松生成可validation的代码。 像大多数devise决定一样,它涉及许多折衷。 最终,CLIdevise人员已经决定,当他们检查可validation性时,他们不希望看到通用约束。

Mehrdad的回答相当好, 我只是想补充一点:

首先,是的,在这种情况下,这只是为了保证validation者的快乐。 抖动当然应该优化掉装箱指令,因为装箱参考types是没有意义的。

但是,有些情况下,要保持核查员的快乐,我们必须引入没有优化的拳击说明。 例如,如果你说:

 class B<T> { public virtual void M<U>(U u) where U : T {...} } class D : B<int> { public override void M<U>(U u) { 

C#编译器知道在DM中,U只能是int。 尽pipe如此,为了可以validation,还有一些情况是你必须装箱来反对,然后取消装箱 。 抖动并不总是优化这些; 我们已经向抖动小组指出这是一个可能的优化,但是情况如此晦涩,以至于不可能为许多实际客户带来巨大的胜利。 他们可能会花费更多时间进行更大规模的优化。