基于这个有趣的问题:Addition of int and uint和Nicholas Carey's answer中提及的常量折叠,我偶然发现了一个看似错误的问题编译器的行为不一致:
请考虑以下代码段:
int i = 1;
uint j = 2;
var k = i - j;
此处编译器正确地将k
解析为long
。这种特殊行为在规范中有明确定义,如前面提到的问题的答案所述。
令我感到惊讶的是,在处理文字常量或常量时,行为会发生变化。阅读Nicholas Carey的answer我意识到这种行为可能不一致所以我检查并确定:
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
。
在处理常量时,是否存在行为不同的原因,或者这是一个小但不幸的错误" (编译器中没有更好的术语)?
答案 0 :(得分:4)
从C# specification version 5,第6.1.9节,常量表达式只允许以下隐式转换
6.1.9隐式常量表达式转换
隐式常量表达式转换允许以下转换:
*int
类型的常量表达式(第7.19节)可以转换为sbyte
,byte
,short
,ushort
,uint
类型,或ulong
,前提是constant-expression的值在目标类型的范围内 •如果constant-expression的值不是负数,则long
类型的常量表达式可以转换为ulong
类型。
请注意,long
转化列表中不包含int
。
问题的另一半是二进制操作只发生少量数字促销:
(来自第7.3.6.2节二进制数字促销):
- 如果任一操作数的类型为十进制,则另一个操作数将转换为十进制类型,或者如果另一个操作数的类型为float或double,则会发生绑定时错误。
- 否则,如果任一操作数的类型为double,则另一个操作数将转换为double类型。
- 否则,如果任一操作数的类型为float,则另一个操作数将转换为float类型。
- 否则,如果任一操作数的类型为ulong,则另一个操作数将转换为ulong类型,如果另一个操作数的类型为sbyte,short,int或long,则会发生绑定时错误。
- 否则,如果任一操作数的类型为long,则另一个操作数将转换为long类型。
- 否则,如果任一操作数的类型为uint而另一个操作数的类型为sbyte,short或int,则两个操作数都将转换为long类型。
- 否则,如果任一操作数的类型为uint,则另一个操作数将转换为uint类型。
- 否则,两个操作数都将转换为int。
请记住:常量禁止int
到long
转换,这意味着两个参数都会被提升为uint
s。
答案 1 :(得分:3)
查看此答案here
问题是你使用的是const。
在存在const的运行时,行为与文字完全相同,或者就好像您只是在代码中对这些数字进行了硬编码一样,因此数字为1和2,因为1在1内,所以它会转换为Uint32 uint32的范围。然后当你试图用uint32减去1 - 2时它会溢出,因为1u - 2u = +4,294,967,295(0xFFFFFFFF)。
允许编译器查看litterals,并解释它们与其他变量不同。因为const永远不会改变,所以它可以保证它无法做到。在这种情况下,它可以保证1在一个uint的范围内,因此它可以隐含地投射它。在正常情况下(没有const)它无法做出保证,
signed int的范围是-2,147,483,648(0x80000000)到+2,147,483,647(0x7FFFFFFF)。
unsigned int的范围是0(0x00000000)到+4,294,967,295(0xFFFFFFFF)。
故事的道德,混合const和var时要小心,你可能得到一些你不期望的东西。