让我们检查为以下通用方法生成的MSIL代码:
public static U BoxValue<T, U>(T value)
where T : struct, U
where U : class
{
return value;
}
查找
.method public hidebysig static !!U BoxValue<valuetype .ctor
([mscorlib]System.ValueType, !!U) T,class U>(!!T 'value') cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: box !!T
IL_0006: unbox.any !!U
IL_000b: ret
}
但是对于上面的通用代码,更有效的IL表示应该是:
IL_0000: ldarg.0
IL_0001: box !!T
IL_0006: ret
从约束条件可知,该值被装入引用类型。 Unbox.any
操作码完全是冗余的,因为在box
操作码后,IL堆栈的值已经是!!U
的有效引用,可以在没有任何拆箱的情况下使用。
为什么C#3.0编译器不使用约束元数据来发出更高效的通用代码? Unbox.any提供了一个小的开销(只比4x-5x慢),但为什么不在这种情况下发出更好的代码呢?
答案 0 :(得分:6)
看起来编译器会这样做,因为验证程序存在一些问题。
您希望编译器生成的IL不可验证,因此C#编译器无法生成它(“不安全”上下文之外的所有C#代码都应该是可验证的。)
“验证类型兼容性”的规则在Ecma规范的第III部分第1.8.1.2.3节中给出。
他们说使用以下规则,类型'S'与类型'T'或(S:= T)验证兼容:
在这些规则中,唯一可能适用于此情况的规则是#3。
但是,#3不适用于您的代码,因为'U'不是'T'的基类,并且它不是'T'的基接口,因此'或'检查返回false。 / p>
这意味着需要执行SOME指令,以便以通过验证程序的方式将盒装T转换为U.
我同意您的观点,即应更改验证规则,以便生成您想要的代码实际上是可验证的。
但从技术上讲,编译器根据ECMA规范做了“正确”的事情。
你应该向微软的某人提出错误。
答案 1 :(得分:3)
这些限制看起来很奇怪:
where T : struct, U
where U : class
T是值类型,但同时必须从作为引用类型的U继承。我想知道什么类型可以满足上述约束,并允许我们称这种方法。