BigInteger неизменен или нет?
-
06-07-2019 - |
Вопрос
В .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 ++
, но очевидно, что ++
само по себе достаточно для изменения значения. Р>
Есть мысли?
Решение
Операторы ++
и -
реализованы в терминах обычных операторов +
и -
, так на самом деле:
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;
После этой операции временная температура утилизируется ГХ, и память освобождается.
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 на месте без возврата нового объекта.