c# – 整体类型促销不一致
using System; public class Tester { public static void Main() { const uint x=1u; const int y=-1; Console.WriteLine((x+y).GetType()); // Let's refactor and inline y... oops! Console.WriteLine((x-1).GetType()); } } 想象一下上面的代码在以下情况中使用: public long Foo(uint x) { const int y = -1; var ptr = anIntPtr.ToInt64() + (x + y) * 4096; return ptr; } 内联y看起来非常安全,但事实并非如此.语言本身的这种不一致是违反直觉的,而且非常危险.大多数程序员只是内联y,但实际上你最终会出现整数溢出错误.事实上,如果你编写如上所述的代码,你很容易让下一个人在同一条代码内联工作,甚至没有考虑过两次. 我认为这是C#的一个非常适得其反的语言设计问题. 第一个问题,在C#规范中定义了这种行为,为什么这样设计? 第二个问题,1.GetType()/( – 1).GetType()给出了System.Int32.那么为什么它与const int y = -1的行为不同? 第三个问题,如果隐式转换为uint,那么我们如何明确地告诉编译器它是一个有符号整数(1i不是有效的语法!)? 最后一个问题,这不可能是语言设计团队所期望的行为(Eric Lippert要编钟吗?),可以吗? 解决方法
此行为由C#标准的第6.1.9节描述,隐式常量表达式转换:
所以你有常数x = 1u;和常量表达式(x – 1). 根据规范,x-1的结果通常是int,但由于常量表达式(即0)的值在uint的范围内,因此将其视为uint. 请注意,这里编译器将1视为无符号. 如果将表达式更改为(x -1),则将-1视为signed,并将结果更改为int. (在这种情况下,– in -1是一个“一元运算符”,它将-1的结果类型转换为int,因此编译器不能再像普通1那样将它转换为uint. 规范的这一部分暗示如果我们将常量表达式更改为x – 2,那么结果将不再是uint,而是转换为int.但是,如果进行了更改,则会收到编译错误,指出结果将溢出uint. 这是因为C#规范的另一部分,在7.19节常量表达式中指出:
在这种情况下,如果进行检查计算会出现溢出,因此编译器会发生故障. 关于这个: const uint x = 1u; const int y = -1; Console.WriteLine((x + y).GetType()); // Long 这与此相同: Console.WriteLine((1u + -1).GetType()); // Long 这是因为-1的类型为int,而1u的类型为uint. 第7.3.6.2节二进制数字促销描述了这个: ?否则,如果任一操作数的类型为uint而另一个操作数的类型为sbyte,short或int,则两个操作数都将转换为long类型. (我省略了与此特定表达式无关的部分.) 附录:我只是想指出常数和非常数值之间的一元减号(又称“否定”)运算符的细微差别. 根据标准:
变量也是如此: var p = -1; Console.WriteLine(p.GetType()); // int var q = -1u; Console.WriteLine(q.GetType()); // long var r = 1u; Console.WriteLine(r.GetType()); // uint 虽然对于编译时常量,如果涉及uint的表达式使用它,则将值1转换为uint,为了将整个表达式保持为uint,-1实际上被视为int. 我同意OP – 这是非常微妙的东西,导致各种惊喜. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |