C# 编译器 +带拳击的通用代码 +限制条件
让我们检查一下为以下泛型方法生成的 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 的开销很小(仅慢 4-5 倍),但为什么不在这种情况下发出更好的代码呢?
Let's examine the MSIL code generated for the following generic method:
public static U BoxValue<T, U>(T value)
where T : struct, U
where U : class
{
return value;
}
Look:
.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
}
But for generic code above, the more efficient IL representation should be:
IL_0000: ldarg.0
IL_0001: box !!T
IL_0006: ret
It is known from the constraints that the value is boxed into reference type. Unbox.any
opcode is completely redundant because after box
opcode the value at IL stack will already be a valid reference to !!U
, that can be used without any unboxing.
Why does C# 3.0 compiler doesn't use constraints metadata to emit more efficient generic code? Unbox.any gives a small overhead (just 4x-5x slower), but why not emit better code in this scenario?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
看起来编译器这样做是因为验证器存在一些问题。
您希望编译器生成的 IL 是不可验证的,因此 C# 编译器无法生成它(“不安全”上下文之外的所有 C# 代码都应该是可验证的)。
“验证类型兼容性”的规则在 Ecma 规范第 III 部分第 1.8.1.2.3 节中给出。
他们说类型“S”与类型“T”或 (S := T) 验证兼容,使用以下规则:
S 的
是一个向量并且两者具有相同的秩。 (此规则处理数组协方差。)
调用约定)是相同的。
在这些规则中,唯一可能适用于本例的规则是#3。
但是,#3 不适用于您的代码,因为“U”不是“T”的基类,也不是“T”的基接口,因此“or”检查返回 false。
这意味着需要执行某些指令才能将装箱的 T 转换为 U 以通过验证程序。
我同意你的观点,应该更改验证规则,以便生成你想要的代码实际上是可验证的。
然而从技术上讲,编译器正在根据 ECMA 规范做“正确”的事情。
您应该向 Microsoft 的某人提交错误。
It looks like the compiler does this because of some issues with the verifier.
The IL that you would like the compiler to generate is not verifiable, and so the C# compiler can't generate it (all C# code outside of "unsafe" contexts should be verifiable).
The rules for "verification type compatibility" are given in Section 1.8.1.2.3, Partion III of the Ecma spec.
They say that a type 'S' is verification compatible with a type 'T' or (S := T) using the following rules:
of S
is a vector and both have the same rank. (This rule deals with array covariance.)
calling convention) are the same.
Of these rules, the only one that might be applicable in this case is #3.
However, #3 does not apply to your code, because 'U' is not a base class of 'T', and it is not a base interface of 'T', so the 'or' check returns false.
This means that SOME instruction needs to be executed in order to convert a boxed T into a U in a way that will pass the verifier.
I would agree with you that the verification rules should be changed, so that generating the code you want is actually verifiable.
Technically, however, the compiler is doing the "correct" thing based on the ECMA spec.
You should file a bug with somebody at Microsoft.
这些约束看起来很奇怪:
T 是值类型,但同时必须继承自引用类型 U。我想知道什么类型可以满足上述约束并允许我们调用这个方法。
These constraints look strange:
T is a value type but in the same time must inherit from U which is a reference type. I wonder what types could satisfy the above constraints and allow us to call this method.