这个问题源于一个错误,我迭代了一个集合,Int64并意外地做了foreach (int i in myCollection).我试图调试令人困惑的问题,当我做一个linq查询,i不是一部分myCollection.
这里有一些令我惊讶的代码:
Int64 a = 12345678912345;
Console.Write((int)a);
Run Code Online (Sandbox Code Playgroud)
我希望编译器给我一个错误.通常的一个是隐式演员表不存在.但不,它根本不介意这一点.甚至没有警告!
(int)a偶然的输出值是1942903641.
我很想知道为什么演员在没有任何警告的情况下被允许,以及它是如何产生这个价值的.有任何想法吗?
Han*_*ant 14
默认情况下,不会检查此类转换.你必须明确地要求它:
Console.Write(checked((int)a)); // Kaboom!
Run Code Online (Sandbox Code Playgroud)
可以通过C#编译器的/ checked选项全局启用已检查的转换.项目模板没有为Debug构建启用此选项是我书中的疏忽,溢出可能非常难以诊断.
但是,您无法解决任何问题,只需使用项目>属性>构建选项卡>高级按钮>勾选"检查算术溢出/下溢"选项.并注意你的foreach循环现在如何使用OverflowException进行轰炸.请记住,当它发生时你会盯着它看几分钟:)这不是一个非常便宜的检查,所以你会想要将它留给Release版本.
默认情况下,C#在处理数字时不检查溢出.这包括诸如从包装int.MaxValue到int.MinValue在加法和乘法,而当你施放longs到int秒.要控制它,请使用checked和uncheckedkeywords或/checked编译器选项.
该值1942903641是long截断为a 时的结果int.它来自该long值的32个最低有效位,作为二进制补码有符号整数.
使用时foreach,重要的是要知道如果声明的类型与枚举的类型不匹配,它会将其视为您转换为该类型.foreach (int i in myCollection)编译成类似的东西int i = (int)myEnumerator.Current;,而不是int i = myEnumerator.Current;.您可以使用foreach (var i in myCollection)以避免将来出现此类错误.var 建议用于循环变量for和foreach语句.
您可以在以下示例中看到各种事物的结果(十六进制输出用于更清楚地显示截断:它们具有相同的结束数字,int只是缺少一些更有效的数字):
checked
{
Int64 a = 12345678912345;
Console.WriteLine(a.ToString("X"));
Console.WriteLine((a % ((long)uint.MaxValue + 1L)).ToString("X"));
try
{
Console.WriteLine(((int)a).ToString("X")); // throws exception
}
catch (Exception e)
{
Console.WriteLine("It threw! " + e.Message);
}
}
unchecked
{
Int64 a = 12345678912345;
Console.WriteLine(a.ToString("X"));
Console.WriteLine((a % (long)Math.Pow(2, 32)).ToString("X"));
Console.WriteLine(((int)a).ToString("X"));
}
Run Code Online (Sandbox Code Playgroud)
这输出:
B3A73CE5B59
73CE5B59
It threw! Arithmetic operation resulted in an overflow.
B3A73CE5B59
73CE5B59
73CE5B59
Run Code Online (Sandbox Code Playgroud)