Question

In .NET 4 beta 2, there is the new Numerics namespace with struct BigInteger. The documentation states that it is an immutable type, as I would have expected.

But I'm a little confused by the post-increment operator (++). This defintely seems to mutate the value. The following while-loop works:

static BigInteger Factorial(BigInteger n)
{
    BigInteger result = BigInteger.One;
    BigInteger b = BigInteger.One;

    while (b <= n)
    {
        result = result * b;
        b++;  // immutable ?
    }
    return result;
}

This is what MSDN has to say about the Increment operator:

Because BigInteger objects are immutable, the Increment operator creates a new BigInteger object whose value is one more than the BigInteger object represented by value. Therefore, repeated calls to Increment may be expensive.

All well and fine, I would have understood if I had to use b = b++ but apparently ++ by itself is enough to change a value.

Any thoughts?

Was it helpful?

Solution

The operators ++ and -- are implemented in terms of the normal + and - operators, so in reality:

b++;

is equivalent to:

var temp = b;
b = b + 1;
<use temp for the expression where b++ was located>

Now, as commented, this might seem like it breaks immutability, but it does not.

You should instead look at this code as doing this:

var temp = b;
b = BigInteger.op_Add(b, 1); // constructs a new BigInteger value
<use temp ...>

This will leave two objects in memory, the original BigInteger value, and the new one, now referenced by b. You can easily check that this is what happens with the following code:

var x = b;
b++;
// now inspect the contents of x and b, and you'll notice that they differ

So the original object did not change, hence it does not break immutability, and to answer the new part of the question, this should be thread-safe.

This is the same thing that happens to strings:

String s1 = s2;
s2 += "More";
// now inspect s1 and s2, they will differ

OTHER TIPS

Since BigInteger is immutable, b++ will be just equivalent to:

BigInteger temp=b;
b=temp+1;

After this operation, temp is recycled by the GC and the memory is freed.

BigInteger b = BigInteger.One;

b++;  // immutable ?

In your example b is a variable, which is just a slot of memory in the current method's stack frame. It is initialised to One, and b++ takes b, creates a new BigInteger (with the incremented value) and returns it. variable b now has the state from the returned new BigInteger.

To be honest immutability as a concept is much clearer when dealing with reference types, because there is an object on the heap whose internal state never changes, so when an operation/method returns a new object with different state it's kind of obvious (e.g. you can do an object reference equality check with object.ReferenceEquals(object, object).

For value types there is no object on the heap, there is just the slot in memory that contains the bits that are the value.

Ok, but what about the unary negation operator that is defined on BigInteger:

public static BigInteger operator -(BigInteger value)
{
    value._sign = -value._sign;
    return value;
}

it appears to break the immutability pattern and directly mutate the BigInteger object. So

b = -b;

actually changes an existing BigInteger in place without returning a new object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top