8

基于这个有趣的问题:添加 int 和 uint并玩弄Nicholas Carey 的回答中提到的常量折叠,我偶然发现了编译器看似不一致的行为:

考虑以下代码片段:

int i = 1;
uint j = 2;
var k = i - j;

这里编译器正确解析klong. 正如前面提到的问题的答案中所解释的那样,这种特殊行为在规范中得到了很好的定义。

令我惊讶的是,在处理文字常量或一般常量时,行为会发生变化。阅读 Nicholas Carey 的回答,我意识到行为可能不一致,所以我检查并确定:

const int i = 1;
const uint j = 2;
var k = i - j; //Compile time error: The operation overflows at compile time in checked mode.
k = 1 - 2u; //Compile time error: The operation overflows at compile time in checked mode.

k在这种情况下被解决为Uint32.

处理常量时行为不同是否有原因,或者这是编译器中的一个小但不幸的“错误”(缺乏更好的术语)?

4

2 回答 2

4

C# 规范版本 5的第 6.1.9 节开始,常量表达式仅允许以下隐式转换

6.1.9 隐式常量表达式转换
隐式常量表达式转换允许以下转换:
* 类型的常量表达式(第 7.19 节)int可以转换为 type sbytebyteshortushortuintulong,前提是常量表达式的值在目标类型的范围。
• type 的常量表达式long可以转换为 type ulong,前提是常量表达式的值不是负数。

请注意,long它不在int转换列表中。

问题的另一半是二进制操作只发生少量数字提升:

(来自第 7.3.6.2 节二进制数字促销):

  • 如果任一操作数是十进制类型,则另一个操作数将转换为十进制类型,或者如果另一个操作数是浮点或双精度类型,则会发生绑定时错误。
  • 否则,如果任一操作数是 double 类型,则另一个操作数将转换为 double 类型。
  • 否则,如果任一操作数是浮点类型,则另一个操作数将转换为浮点类型。
  • 否则,如果任一操作数的类型为 ulong,则另一个操作数将转换为 ulong 类型,或者如果另一个操作数的类型为 sbyte、short、int 或 long,则会发生绑定时错误。
  • 否则,如果任一操作数是 long 类型,则将另一个操作数转换为 long 类型。
  • 否则,如果任一操作数为 uint 类型,而另一个操作数为 sbyte、short 或 int 类型,则这两个操作数都将转换为 long 类型。
  • 否则,如果任一操作数是 uint 类型,则将另一个操作数转换为 uint 类型。
  • 否则,两个操作数都转换为 int 类型。

记住:禁止对常量int进行 tolong转换,这意味着两个 args 都被提升为uints。

于 2014-10-15T15:58:35.937 回答
3

在这里查看这个答案

问题是您使用的是 const。

在运行时,当存在 const 时,行为与文字完全相同,或者好像您只是在代码中对这些数字进行了硬编码,因此由于数字是 1 和 2,它会转换为 Uint32,因为 1 在uint32。然后,当您尝试用 uint32 减去 1 - 2 时,它会溢出,因为 1u - 2u = +4,294,967,295 (0xFFFFFFFF)。

允许编译器查看文字,并以与其他变量不同的方式解释它们。由于 const 永远不会改变,它可以做出其他方式无法做出的保证。在这种情况下,它可以保证 1 在 uint 的范围内,因此它可以隐式转换它。在正常情况下(没有 const)它不能保证,

有符号整数的范围从 -2,147,483,648 (0x80000000) 到 +2,147,483,647 (0x7FFFFFFF)。

无符号整数的范围从 0 (0x00000000) 到 +4,294,967,295 (0xFFFFFFFF)。

故事的道德,在混合 const 和 var 时要小心,你可能会得到意想不到的东西。

于 2014-10-15T15:39:45.893 回答