4

Just noticed that the unchecked context doesn't work when working with a BigInteger, for instance:

unchecked
{
    // no exception, long1 assigned to -1 as expected
    var long1 = (long)ulong.Parse(ulong.MaxValue.ToString());
}

unchecked
{
    var bigInt = BigInteger.Parse(ulong.MaxValue.ToString());

    // throws overflow exception
    var long2 = (long)bigInt;
}

Any idea why that's the case? Is there something special with the way big integers are converted to other primitive integer types?

Thanks,

4

3 回答 3

18

C# 编译器根本不知道 BigInteger 在逻辑上是“整数类型”。它只看到一个用户定义的类型,其中用户定义的显式转换为 long。从编译器的角度来看,

long long2 = (long)bigInt;

与以下内容完全相同:

long long2 = someObject.SomeMethodWithAFunnyNameThatReturnsALong();

它无法进入该方法并告诉它停止抛出异常。

但是当编译器看到

int x = (int) someLong;

编译器正在生成执行转换的代码,因此它可以选择生成它认为合适的检查或未检查代码。

请记住,“已选中”和“未选中”在运行时无效;当控制进入未经检查的上下文时,CLR 不会进入“未经检查的模式”。“checked”和“unchecked”是给编译器的关于在块内生成什么样的代码的指令。它们只在编译时起作用,BigInt 转换为 long 的编译已经发生。它的行为是固定的。

于 2011-08-27T13:40:00.090 回答
4

OverflowException实际上是由定义在 上的显式转换运算符抛出的BigInteger。它看起来像这样:

int num = BigInteger.Length(value._bits);
if (num > 2)
{
    throw new OverflowException(SR.GetString("Overflow_Int64"));
}

换句话说,它以这种方式处理溢出,而不管checkedorunchecked上下文。文档实际上是这样说的。

更新:当然,埃里克是这方面的最终决定。请去阅读他的帖子:)

于 2011-08-27T13:36:51.503 回答
2

文档明确指出它将OverflowException在这种情况下抛出。检查的上下文仅对 C# 编译器发出的“本机”算术运算产生影响——它不包括调用显式转换运算符。

要“安全地”执行转换,您必须将其与long.MaxValuelong.MinValue首先检查它是否在范围内。为了获得溢出到负面的效果,我怀疑您必须首先在其中执行使用位运算符BigInteger。例如:

using System;
using System.Numerics;

class Program
{
    static void Main(string[] args)
    {
        BigInteger bigValue = new BigInteger(ulong.MaxValue);

        long x = ConvertToInt64Unchecked(bigValue);
        Console.WriteLine(x);
    }

    private static readonly BigInteger MaxUInt64AsBigInteger
        = ulong.MaxValue;

    private static long ConvertToInt64Unchecked(BigInteger input)
    {
        unchecked
        {
            return (long) (ulong) (input & MaxUInt64AsBigInteger);
        }
    }
}
于 2011-08-27T13:42:59.777 回答