BigInteger неизменен или нет? - PullRequest
9 голосов
/ 28 октября 2009

В .NET 4 beta 2 появилось новое пространство имен Numerics с struct BigInteger. Документация гласит, что это неизменяемый тип, как я и ожидал.

Но меня немного смущает оператор постинкремента (++). Это определенно, кажется, мутирует значение. Работает следующий цикл 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 говорит об операторе приращения:

Потому что объекты BigInteger неизменный, оператор приращения создает новый объект BigInteger, чей значение на один больше, чем BigInteger объект, представленный значением. Поэтому повторные звонки в инкремент может быть дорогим.

Все хорошо, я бы понял, если бы мне пришлось использовать b = b++, но, очевидно, одного ++ достаточно для изменения значения.

Есть мысли?

Ответы [ 4 ]

14 голосов
/ 28 октября 2009

Операторы ++ и -- реализованы в терминах обычных операторов + и -, поэтому в действительности:

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
3 голосов
/ 28 октября 2009

Поскольку BigInteger неизменен, b ++ будет эквивалентен:

BigInteger temp=b;
b=temp+1;

После этой операции временная температура утилизируется ГХ, и память освобождается.

0 голосов
/ 22 марта 2019
BigInteger b = BigInteger.One;

b++;  // immutable ?

В вашем примере b - это переменная, которая представляет собой просто слот памяти в кадре стека текущего метода. Он инициализируется в One, и b ++ берет b, создает новый BigInteger (с увеличенным значением) и возвращает его. переменная b теперь имеет состояние из возвращенного нового BigInteger.

Честно говоря, неизменность как концепция намного яснее при работе со ссылочными типами, потому что в куче есть объект, внутреннее состояние которого никогда не меняется, поэтому, когда операция / метод возвращает новый объект с другим состоянием, это становится очевидным (например, вы можете выполнить проверку на равенство ссылок на объект с помощью object.ReferenceEquals (object, object).

Для типов значений в куче нет объектов, в памяти есть только слот, содержащий биты, являющиеся значением.

0 голосов
/ 22 марта 2019

Хорошо, но как насчет унарного оператора отрицания, определенного в BigInteger:

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

кажется, что нарушает шаблон неизменяемости и напрямую мутирует объект BigInteger. Так

b = -b;

фактически изменяет существующий BigInteger на место без возврата нового объекта.

...