字节溢出会计算为零而不是抛出异常?

3
static void Main(string[] args)
{
  int n;
  byte b;
  n = 256;
  b = (byte) n;
  Console.WriteLine(b); //0
}

C#的字节范围是0到255,因此我试图将一个值为256的整数强制转换为字节,并看看会发生什么。

令人惊讶的是,它返回0而不是255,或者更好的是给我一个溢出异常?

更新: 我正在尝试在macOS上使用Mono,如果有影响,我的.NET Framework版本是4.7。


256的二进制表示为1 0000 0000,一个字节只包含8个位,因此当您将256转换为字节时,左侧的1将被忽略。 - Muhammad Vakili
1
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/checked-compiler-option - Hans Passant
2个回答

6
这是期望的行为。如果您考虑一下,256在二进制中是一个“1”后跟8个零。当你除去除最低有效8位之外的所有内容时,你得到8个零,即值0。
来自C#语言规范§6.2.1:
对于从整数类型到另一整数类型的转换,处理取决于转换发生的溢出检查上下文(§7.6.12):
- 在“checked”上下文中,如果源操作数的值在目标类型的范围内,则转换成功,但如果源操作数的值超出目标类型的范围,则会抛出System.OverflowException异常。 - 在“unchecked”上下文中,转换总是成功,并按以下方式进行:
- 如果源类型大于目标类型,则通过丢弃其“额外”的最高有效位来截断源值。然后将结果视为目标类型的值。
如果您想要一个异常,则可以使用“checked”。
b = checked((byte) n);

太好了!但这是否意味着为了在处理数字时具有绝对可预测的结果,建议在每次算术操作中都打开checked选项?或进行类型转换? - Isaac
如果你希望在溢出发生时始终引发异常,那么只要有可能溢出就应该放置“checked”。但有时候,"环绕"也许更可取。@Isaac - Sweeper
出于好奇,由于这涉及到另一个额外的检查,它会以任何方式影响性能吗?或者是可以忽略的? - Isaac
2
@Isaac 这确实增加了一个额外的检查,但是你应该只在实际遇到性能问题并且确定是由checked语句引起的时候才处理它们,而不是其他代码引起的,这种情况不太可能发生。 - Sweeper
1
如果你想让它总是抛出异常,你可以添加-checked编译器选项,在.csproj文件中它应该是<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> - iakobski

2
我想补充一下之前的回答。
看一下这个:
255 -> 11111111 +
001 -> 00000001
256 -> 100000000
如你所见,我们有256的二进制格式,但由于你的数字是8位,1无法存储。这留下了00000000,即零。
这更多是理论问题,而不是特定于C#的问题。但我认为这很重要理解。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接