为什么C#编译器不抱怨这个明显的';坏的';铸造
我不明白为什么下面的代码会编译为什么C#编译器不抱怨这个明显的';坏的';铸造,c#,.net,compiler-construction,clr,overflow,C#,.net,Compiler Construction,Clr,Overflow,我不明白为什么下面的代码会编译 public void Overflow() { Int16 s = 32767; s = (Int16) (s + 1); } 在编译时,(s+1)显然不再是Int16,因为我们知道s的值 CLR允许强制转换: 自成一格 或任何基类型(因为它是安全的) 因为Int32不是Int16,Int16也不是Int32的基类型 问题:那么为什么编译器在上述转换中没有失败呢?你能从CLR和编译器的角度解释一下吗 感谢表达式s+1的类型是Int32-在
public void Overflow()
{
Int16 s = 32767;
s = (Int16) (s + 1);
}
在编译时,(s+1)显然不再是Int16,因为我们知道s的值 CLR允许强制转换:
- 自成一格
- 或任何基类型(因为它是安全的)
感谢表达式
s+1
的类型是Int32
-在执行加法之前,两个操作数都转换为Int32
。因此,您的代码相当于:
public void Overflow()
{
Int16 s = 32767;
s = (Int16) ((Int32) s + (Int32) 1);
}
因此溢出实际上只发生在显式转换中
或者,换一种说法:因为语言规范这么说。您应该描述以下情况之一:
- 为什么您认为编译器违反了语言规范
- 您建议对语言规范进行的确切更改
s = s + 1;
当s
是Int16
时,无论s
的值是什么。没有Int16运算符+(Int16,Int16)
运算符-如C#4规范第7.8.4节所示,整数加法运算符为:
int operator +(int x, int y);
uint operator +(uint x, uint y);
long operator +(long x, long y);
ulong operator +(ulong x, ulong y);
“在编译时,(s+1)显然不再是Int16,因为我们知道s的值。”
我们知道s+1的值对于短时间来说太大了;编译器没有。编译器知道三件事:
s+1
;但是,由于您显式地请求了强制转换,编译器不会抱怨—它完全按照您的要求执行
此外,在许多情况下,“环绕”溢出(或模-2n算术)是有意且必需的。如果显式转换为较小的类型,编译器将合理地假定它是必需的
这取决于程序员为操作选择合适的类型,如果溢出是不需要的,
float
,double
或decimal
可能是比系统限制的整数类型更合适的算术类型。感谢大家的解释
我还想引用Jeffry Richter的书中的一句话,解释为什么当您尝试将Int16转换为Int32时,编译器不会失败,而它们不是彼此派生的:
第116页:
“(…)C#编译器
熟悉基本类型,并在编译时应用自己的特殊规则
换句话说,编译器识别常见的编程模式和
生成必要的IL以使编写的代码按预期工作。“是。而且语言规范也没有说编译器应该进行数据流分析,并且“知道”s将有一个常量值(除非您实际上将其声明为const,而您没有)。Jus作为注释-s+1是int32,因为“1”是int32。@usr:如果它不做任何分析,那么为什么“s=s+1;”不编译?@TomTom:我想原因是你超出了Int16@pencilCake:
s=s+1
不编译,因为s+1
的结果是Int32
,并且没有从Int32
到Int16
的隐式转换。没有强制转换,代码不会编译,但它与范围无关,与类型不匹配有关。@Michael:谢谢您的澄清(C++程序员,不经常使用C#user!)。我的意思是“显式请求强制转换”,而不是“更改变量类型”,并不是建议仅省略强制转换。关键是如果编译器告诉您存在类型不匹配,而您“解决”它通过强制转换为不适当的类型而不是更改指定变量的类型来实现,编译器无法“检测”该类型,因为这可能是语义错误,也可能是应用程序要求。您可以使用强制转换来抑制溢出。如果需要,请使用Convert.ToInt16()相反。我无意冒犯杰弗里·里克特,但这是一个相当蹩脚的解释。你已经收到了一些更好的答案。奇怪的是,long
可以隐式转换为float
,但是double
不能,尽管事实上对于e