BigInteger是不可变的吗?
-
06-07-2019 - |
题
在.NET 4 beta 2中,新的Numerics命名空间带有 struct BigInteger
。 文档指出正如我所料,它是一种不可变的类型。
但我对后增量运算符( ++
)感到困惑。这种defintely似乎改变了价值。以下while循环有效:
static BigInteger Factorial(BigInteger n)
{
BigInteger result = BigInteger.One;
BigInteger b = BigInteger.One;
while (b <= n)
{
result = result * b;
b++; // immutable ?
}
return result;
}
这就是MSDN对Increment运算符的看法:
因为BigInteger对象是 不可变,Increment运算符 创建一个新的BigInteger对象 value比BigInteger多一个 由价值代表的对象。 因此,反复调用Increment 可能很贵。
一切都很好,我会理解我是否必须使用 b = b ++
,但显然 ++
本身足以改变一个值。
有什么想法吗?
解决方案
运算符 ++
和 -
是根据普通的 +
和 -
运算符实现的,所以实际上:
b++;
相当于:
var temp = b;
b = b + 1;
<use temp for the expression where b++ was located>
现在,正如评论所说,这似乎打破了不变性,但事实并非如此。
您应该将此代码视为执行此操作:
var temp = b;
b = BigInteger.op_Add(b, 1); // constructs a new BigInteger value
<use temp ...>
这将在内存中留下两个对象,即原始BigInteger值,以及现在由b引用的新对象。您可以轻松地检查这是以下代码所发生的情况:
var x = b;
b++;
// now inspect the contents of x and b, and you'll notice that they differ
因此原始对象没有改变,因此它不会破坏不变性,并且回答问题的新部分,这应该是线程安全的。
这与字符串相同:
String s1 = s2;
s2 += "More";
// now inspect s1 and s2, they will differ
其他提示
由于BigInteger是不可变的,因此b ++将等同于:
BigInteger temp=b;
b=temp+1;
执行此操作后,GC将回收temp并释放内存。
BigInteger b = BigInteger.One;
b++; // immutable ?
在你的例子中,b是一个变量,它只是当前方法堆栈帧中的一个内存槽。它初始化为One,b ++接受b,创建一个新的BigInteger(具有递增的值)并返回它。变量b现在具有返回的新BigInteger的状态。
说实话,作为一个概念的不变性在处理引用类型时要清楚得多,因为堆上有一个内部状态永远不会改变的对象,所以当一个操作/方法返回一个具有不同状态的新对象时,它显而易见(例如,您可以使用object.ReferenceEquals(object,object)执行对象引用相等性检查。
对于值类型,堆上没有对象,内存中只有包含值的位。
好的,但是在BigInteger上定义的一元否定运算符呢?
public static BigInteger operator -(BigInteger value)
{
value._sign = -value._sign;
return value;
}
它似乎打破了不变性模式并直接改变了BigInteger对象。所以
b = -b;
实际上在不返回新对象的情况下更改现有的BigInteger。