我对以下示例中的C#编译器行为感到惊讶:
int i = 1024;
uint x = 2048;
x = x+i; // A error CS0266: Cannot implicitly convert type 'long' to 'uint' ...
Run Code Online (Sandbox Code Playgroud)
似乎int + uint可以溢出.但是,如果uint更改为int,则错误消失,就像int + int无法溢出一样:
int i = 1024;
int x = 2048;
x = x+i; // OK, int
Run Code Online (Sandbox Code Playgroud)
而且,uint + uint = uint:
uint i = 1024;
uint x = 2048;
x = x+i; // OK, uint
Run Code Online (Sandbox Code Playgroud)
这似乎完全模糊不清.
为什么int + int = int和uint + uint = uint,但是int + uint = long?
这个决定的动机是什么?
Eri*_*ert 14
为什么int + int = int和uint + uint = uint,但int + uint = long?这个决定的动机是什么?
这个问题的措辞意味着设计团队希望 int + uint很长的预设,并选择类型规则来实现这个目标.这个预设是错误的.
相反,设计团队认为:
以及许多其他考虑因素,例如设计是否适用于可调试,可维护,可版本化的程序等等.(我注意到我没有参加这个特别的设计会议,因为它早于我在设计团队的时间.但我已经阅读了他们的笔记,并了解了在此期间设计团队所关注的事情.)
调查这些问题导致了当前的设计:算术运算定义为int + int - > int,uint + uint - > uint,long + long - > long,int可以转换为long,uint可以转换长,等等.
一个后果,这些决定是加入UINT + INT时,重载长+长的选择是最接近的配对,而长+长很长,因此UINT + INT长.
使uint + int有一些更不同的行为,你可能认为更合理的不是团队的设计目标,因为混合有符号和无符号值是第一个,在实践中很少见,其次,几乎总是一个bug.设计团队可以为有符号和无符号的一个,两个,四个和八个字节整数的每个组合添加特殊情况,以及char,float,double和decimal,或者那些数百个案例的任何子集,但是有效违背简单的目标.
简而言之,一方面,我们有大量的设计工作来制作一个功能,我们希望没有人能够以大规模复杂的规格为代价实际使用更容易.另一方面,我们有一个简单的规范,在极少数情况下产生一种不寻常的行为,我们期望在实践中没有人遇到.鉴于这些选择,您会选择哪个?C#设计团队选择了后者.
Eri*_* J. 12
这是数字类型的重载解析的表现
数字提升包括自动执行预定义一元和二元数字运算符的操作数的某些隐式转换.数字提升不是一种独特的机制,而是将重载决策应用于预定义运算符的效果.尽管可以实现用户定义的运算符以显示类似的效果,但数字促销特别不会影响用户定义的运算符的评估.
http://msdn.microsoft.com/en-us/library/aa691328(v=vs.71).aspx
如果你看看
long operator *(long x, long y);
uint operator *(uint x, uint y);
Run Code Online (Sandbox Code Playgroud)
从该链接,您可以看到这两个可能的重载(示例引用operator *,但同样如此operator +).
的uint隐式转换为一个long用于重载解析,如int.
从uint到long,ulong,float,double或decimal.
从int到long,float,double或decimal.
http://msdn.microsoft.com/en-us/library/aa691282(v=vs.71).aspx
这个决定的动机是什么?
设计团队的成员可能会回答这个问题.Eric Lippert,你在哪里?:-)请注意,@ Nicolas的推理下面的推理非常合理,两个操作数都转换为"最小"类型,可以包含每个操作数的全部值.
Nic*_*rey 11
简短的回答是"因为标准说,它应该是这样",内见信息 ISO 23270.该的§14.2.5.2 规范 §13.1.2.(隐式数字转换)说:
隐式数字转换是:
...
- 从
int到long,float,double,或decimal.- 从
uint到long,ulong,float,double,或decimal....
从转换
int,uint,long或ulong到float从long或ulong到double可能会导致精确度的损失,但绝不会导致幅度的损失. 其他隐式数字转换永远不会丢失任何信息.(我的)
[稍微]更长的答案是您要添加两种不同的类型:32位有符号整数和32位无符号整数:
所以类型不兼容,因为一个int不能包含任何任意uint和一个uint不能包含任意的任何int.它们被隐式转换(扩展转换,根据§13.1.2的要求,没有信息丢失)到下一个可以包含两者的最大类型:long在这种情况下,a是带符号的64位整数,具有域 - 9,223,372,036,854,775,808(0x8000000000000000) - + 9,223,372,036,854,775,807(0x7FFFFFFFFFFFFFFF).
编辑注意:除此之外,执行此代码:
var x = 1024 + 2048u ;
Console.WriteLine( "'x' is an instance of `{0}`" , x.GetType().FullName ) ;
Run Code Online (Sandbox Code Playgroud)
不会产生long原始海报的例子.相反,产生的是:
'x' is an instance of `System.UInt32`
Run Code Online (Sandbox Code Playgroud)
这是因为不断折叠.表达式中的第一个元素1024没有后缀,因此是一个int,而表达式中的第二个元素2048u是a uint,根据规则:
- 如果文字没有后缀,它具有第一这些类型的,其中它的值可以表示的:
int,uint,long,ulong.- 如果文字后缀为
U或u,则它具有这些类型中的第一个,其值可以表示为:uint,ulong.
并且由于优化器知道值是什么,因此总和被预先计算并评估为a uint.
一致性是小脑袋的大人物.
我认为编译器的行为非常符合逻辑和预期.
在以下代码中:
int i;
int j;
var k = i + j;
Run Code Online (Sandbox Code Playgroud)
此操作确实存在过载,因此k也是如此int.添加两个uint,两个byte或你有什么相同的逻辑适用.编译器的工作很简单,很高兴因为重载决策找到了完全匹配.编写此代码的人很可能希望k成为一个int并且知道在某些情况下操作可能会溢出.
现在考虑一下你要问的案例:
uint i;
int j;
var k = i + j;
Run Code Online (Sandbox Code Playgroud)
编译器看到了什么?那么它会看到一个没有匹配过载的操作; 没有运算符+重载将a int和a uint作为其两个操作数.因此,重载决策算法继续进行并尝试查找可能有效的运算符重载.这意味着它必须找到一个重载,其中涉及的类型可以"保持"原始操作数; 即,既i和j必须隐式转换为所述类型.
编译器无法隐式转换uint为,int因为此类转换不会存在.它无法隐式转换int为uint其中之一,因为转换也不存在(两者都可能导致幅度发生变化).所以它唯一的选择就是选择第一个能够"保持"两种操作数类型的更广泛的类型,在这种情况下就是这种类型long.一旦两个操作数被隐式转换为long k存在long是显而易见的.
这种行为的动机是,IMO,选择最安全的可用选项而不是第二次猜测可疑编码器的意图.编译器无法对编写此代码的人期望的内容做出有根据的猜测k.一个int?好吧,为什么不uint呢?两种选择看起来同样糟糕.编译器选择唯一的逻辑路径; 安全的:long.如果编码器想要k成为其中一个,int或者unit他只需要显式地转换其中一个操作数.
最后,但并非最不重要的是,C#编译器的重载决策算法在决定最佳过载时不考虑返回类型.因此,将操作结果存储在a中的事实uint与编译器完全无关,并且在重载解析过程中无任何影响.
这是我的所有猜测,我可能完全错了.但它确实看起来逻辑推理.
| 归档时间: |
|
| 查看次数: |
3817 次 |
| 最近记录: |