为什么C#中的BigInteger是一个结构,如果它有一个无界的大小

本文关键字:有一个 如果 结构 一个 BigInteger 中的 为什么 | 更新日期: 2023-09-27 18:25:28

为什么BigInteger在C#中被声明为ValueType(结构)?它似乎与声明为引用类型的字符串类型非常相似。

两者都是不可变的(值类型)。两者都可以任意大。

我听到的建议是,一个结构永远不应该超过16字节。BigInteger可能会变得比16字节大得多,我认为这会使频繁的操作变得非常慢,因为它总是按值复制的。

为什么C#中的BigInteger是一个结构,如果它有一个无界的大小

复制BigInteger不会导致底层数据被复制。相反,只复制对数据的引用。

由于BigInteger值是不可变的,因此两个或多个值共享一个公共数据缓冲区是安全的。

BigInteger有两个实例字段:

  • int _sign-可能告诉它是正值还是负值
  • uint[] _bits-这是对数据缓冲区的引用

int是4个字节,参考是8个字节(在64位系统上)。因此,BigInteger的大小是≤16字节。

如果您查看BigInteger的源代码,并将其剥离为仅实例级字段(将计入其大小的内容),则该类的所有内容都是

public struct BigInteger : IFormattable, IComparable, IComparable<BigInteger>, IEquatable<BigInteger>                             
{
    internal int _sign;
    internal uint[] _bits;
}

因此,_sign有4个字节,uint[]有4或8个字节,这取决于您是在32位还是64位系统上,因为数组是引用类型。这将为您提供总共8或12个字节,远低于建议的16个字节。(注意:出于优化原因,CLR将把12字节版本填充为16,使其成为8的倍数)

当创建新的BigInteger时,_bits数组将在两个实例之间共享。因为类型是不可变的(不能更改_bits的任何单元格的值),所以两个副本共享数组是安全的。

以下是BigInteger:的字段

// For values int.MinValue < n <= int.MaxValue, the value is stored in sign
// and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits
internal int _sign;
internal uint[] _bits;

因此,一个int和一个uint[],这是一个引用类型。类型本身不能任意增大。在x86上为8字节,在x64上为16字节(字段为12字节+填充4字节)。

string和数组是框架中唯一具有不同大小并在运行时进行特殊大小写的类型。

至于回答这个问题:使用struct会减少开销。在两个字段上使用class包装器会导致更多的间接性和更大的GC压力,这是没有充分理由的。此外,BigInteger在语义上是一个值。

结构的大小之所以重要,是因为每次从一个函数传递到另一个函数时,都必须复制整个结构。如果不是因为复制,没有人会在乎。

然而,BigInteger由两部分组成:

  • 实际的结构,即当您传递BigInteger时被复制的部分,它相当小,以及

  • 位的数组,其长度为任意,但每次复制结构时都会复制而不是

因此,当你通过BigInteger时,会发生以下情况:

复制前:

[BigInteger instance 1] ---------> [array of bits]

复制后:

[BigInteger instance 1] ---------> [array of bits]
                            |
[BigInteger instance 2] ----+

请注意,总是只有一个位数组。

相关文章: