ike C#具有为.NET虚拟机生成代码所需的约束。CLI规范中详细描述了这些规则(并且非常容易理解)。它是Ecma-335规范,您可以免费下载

ike C#具有为.NET虚拟机生成代码所需的约束。CLI规范中详细描述了这些规则(并且非常容易理解)。它是Ecma-335规范,您可以免费下载,c#,types,integer-arithmetic,C#,Types,Integer Arithmetic,转到第三部分,第3.1和3.2章。它们描述了可用于执行加法的两条IL指令,add和add.ovf。单击表2“二进制数字操作”的链接,它描述了这些IL指令允许的操作数。请注意,这里只列出了一些类型。byte和short以及所有无符号类型都丢失。只允许int、long、IntPtr和浮点(float和double)。例如,使用x标记的附加约束时,不能将int添加到long。这些约束并不完全是人为的,它们是基于您可以在可用硬件上合理高效地完成的事情 为了生成有效的IL,任何托管编译器都必须处理这个问题

转到第三部分,第3.1和3.2章。它们描述了可用于执行加法的两条IL指令,
add
add.ovf
。单击表2“二进制数字操作”的链接,它描述了这些IL指令允许的操作数。请注意,这里只列出了一些类型。byte和short以及所有无符号类型都丢失。只允许int、long、IntPtr和浮点(float和double)。例如,使用x标记的附加约束时,不能将int添加到long。这些约束并不完全是人为的,它们是基于您可以在可用硬件上合理高效地完成的事情


为了生成有效的IL,任何托管编译器都必须处理这个问题。这并不困难,只需将ushort转换为表中较大的值类型,这种转换总是有效的。C#编译器选择int,即表中出现的下一个较大的类型。或者通常,将任意操作数转换为下一个最大值类型,以便它们都具有相同的类型并满足表中的约束

然而,现在有了一个新问题,这个问题让C#程序员非常疯狂。添加的结果是升级类型。在您的例子中,这将是int。因此,添加两个ushort值,例如0x9000和0x9000,将得到一个完全有效的int结果:0x12000。问题是:这是一个不符合ushort的值。值溢出。但是它在IL计算中没有溢出,它只是在编译器试图将其重新填充到ushort中时溢出。0x12000被截断为0x2000。一个令人困惑的不同值,只有当你用2或16个手指而不是10个手指计数时才有意义

值得注意的是add.ovf指令没有处理这个问题。它是用于自动生成溢出异常的指令。但事实并非如此,转换后整数的实际计算并没有溢出

这就是真正的设计决策发挥作用的地方。老前辈们显然认为简单地将int结果截断为ushort就是一个bug工厂。当然是这样。他们决定你必须承认你知道加法可能会溢出,如果它发生了也没关系。他们让它成为你的问题,主要是因为他们不知道如何让它成为自己的问题,并且仍然生成高效的代码。你必须投。是的,那太令人恼火了,我相信你也不想遇到这个问题

值得注意的是,VB.NET设计者对这个问题采取了不同的解决方案。他们实际上把它当成了自己的问题,没有推卸责任。您可以添加两个UShort并将其分配给一个UShort,而无需强制转换。区别在于VB.NET编译器实际上会生成额外的IL来检查溢出情况。这不是便宜的代码,使每一个简短的加法的速度大约是原来的3倍。但除此之外,这也解释了为什么微软保留了两种功能非常相似的语言

长话短说:您付出了代价,因为您使用的类型与现代cpu架构不太匹配。这本身就是使用uint而不是ushort的一个很好的理由。要从ushort中获得牵引力是很困难的,在操作它们的成本影响到内存节省之前,您将需要很多。不仅仅是因为有限的CLI规范,x86内核还需要额外的cpu周期来加载16位值,因为机器代码中有操作数前缀字节。实际上,我不确定今天是否仍然是这样,过去我仍然关注计数周期。一年前有只狗



请注意,通过让C#编译器生成与VB.NET编译器相同的代码,您可以更好地处理这些丑陋而危险的强制转换。所以当演员被证明是不明智的时候,你会得到一个OverflowException。使用项目>属性>构建选项卡>高级按钮>勾选“检查算术溢出/下溢”复选框。只是为了调试构建。为什么这个复选框没有被项目模板自动打开是另一个非常令人迷惑的问题,顺便说一句,这是一个很久以前就做出的决定。

(参见J.Skeets答案)我从微软的c#规范中更新了我的答案,这确实证实了我的发现,但它并不能真正解释为什么会发生。@lesderid:我不明白。发生这种情况是因为您使用的编译器符合C#规范。也许您在问第7.3.6.2节背后的原因,以及为什么规则停止在int,而不继续使用较小的类型?是的,我感兴趣的是为什么规范中说两个操作数都应该转换为int。我只是添加了编译器错误来澄清,因为我不确定这是规范还是实现决策。我可以猜,但我真的不知道答案,因此,您应该编辑您的问题,询问语言规则为何如此。这仍然不能解释为什么它会变成int而不是uint。@lesderid,这是由于c#规范(7.8.4,第192页)中定义的运算符+重载优先级造成的。在这种情况下,第一个重载签名是int运算符+(int x,int y),然后是它的uint运算符+(uint x,uint y)。这就是为什么它变成int而不是uint“这并不难,简单地把ushort转换成uint,一个总是有效的转换。”的确,这也是我的想法,但它不会转换成uint,而是转换成int,这还没有人能真正解释。不过,这是一个很好的回答!是的,我搞错了,修好了。看看我指的表,它没有无符号类型。啊,我明白了。我想这回答了我的问题!不过,我要奖励Habib.OSU赏金,因为他完全回答了问题
ushort a = 1;
ushort b = 2;

ushort c = a + b; // <- "Cannot implicitly convert type 'int' to 'ushort'. An explicit conversion exists (are you missing a cast?)"
uint d = a + b; // <- "Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?)"

int e = a + b; // <- Works!
ushort x = 5, y = 12;
ushort z = x + y;   // Error: conversion from int to ushort