为什么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允许强制转换:

  • 自成一格
  • 或任何基类型(因为它是安全的)
因为Int32不是Int16,Int16也不是Int32的基类型

问题:那么为什么编译器在上述转换中没有失败呢?你能从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'
  • 您为其指定了一个有效的短常量
  • 您在两个值之间执行了整数运算,这两个值隐式转换为int
  • 是的,在这种特定的情况下,当您进行类型转换时,确定结果太大而无法放入一个简短的文本中是很简单的,但是要确定编译器需要在编译时执行算术,然后对结果执行类型转换验证检查。除了极少数例外(在规范中都显式调用,主要涉及零值常量),编译器不检查操作结果,只检查操作类型和操作类型是否正确

    此外,您列出的编译器允许强制转换的情况也远远不够。编译器允许在各种情况下进行类型转换,其中许多情况对CLR完全不可见。例如,对于几乎每种数字类型,语言中都内置了隐式和显式类型转换。查找有关类型转换规则的其他信息的好地方:

    一般来说,cast会说“我是故意这么做的,不要抱怨”,因此编译器抱怨是令人惊讶的行为

    事实上,由于参数的隐式提升,并没有溢出。但是,强制转换会截断32位结果,因此结果在算术上不等于
    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