É BigInteger imutável ou não?
-
06-07-2019 - |
Pergunta
Em .NET 4 beta 2, há o novo namespace Numerics com struct BigInteger
. estados do href="http://msdn.microsoft.com/en-us/library/system.numerics.biginteger%28VS.100%29.aspx" rel="nofollow noreferrer"> documentação que é um tipo imutável, como eu teria esperado.
Mas estou um pouco confuso com o operador de pós-incremento (++
). Isso definitivamente parece transformar o valor. As obras seguinte while-loop:
static BigInteger Factorial(BigInteger n)
{
BigInteger result = BigInteger.One;
BigInteger b = BigInteger.One;
while (b <= n)
{
result = result * b;
b++; // immutable ?
}
return result;
}
Este é o MSDN tem a dizer sobre o operador de incremento:
Como os objetos são BigInteger imutável, o operador de incremento cria um novo objeto BigInteger cujas valor é mais um do que o BigInteger objecto representado pelo valor. Portanto, repetido chamadas para Incremento pode ser caro.
Tudo muito bem, eu teria entendido se eu tivesse que usar b = b++
mas aparentemente ++
por si só é suficiente para alterar um valor.
Qualquer pensamento?
Solução
Os operadores ++
e --
são implementadas em termos dos operadores +
e -
normais, assim, na realidade:
b++;
é equivalente a:
var temp = b;
b = b + 1;
<use temp for the expression where b++ was located>
Agora, como comentado, isso pode parecer quebra imutabilidade, mas isso não acontece.
Você deve em vez olhar para este código como fazer isso:
var temp = b;
b = BigInteger.op_Add(b, 1); // constructs a new BigInteger value
<use temp ...>
Isso vai deixar dois objetos na memória, o valor original BigInteger, e o novo, agora referenciados por b. Você pode facilmente verificar que este é o que acontece com o seguinte código:
var x = b;
b++;
// now inspect the contents of x and b, and you'll notice that they differ
Assim, o objeto original não se alterou, portanto, ele não quebrar imutabilidade, e para atender a nova parte da pergunta, esta deve ser thread-safe.
Esta é a mesma coisa que acontece com strings:
String s1 = s2;
s2 += "More";
// now inspect s1 and s2, they will differ
Outras dicas
Desde BigInteger é imutável, b ++ será apenas o equivalente a:
BigInteger temp=b;
b=temp+1;
Após esta operação, a temperatura é reciclado pela GC e a memória é liberada.
BigInteger b = BigInteger.One;
b++; // immutable ?
No seu exemplo b é uma variável, que é apenas um slot de memória do quadro de pilha do método atual. Ele é inicializado para um, e b ++ leva b, cria um novo BigInteger (com o valor incrementado) e devolve-lo. variável b agora tem estado a partir do novo BigInteger retornado.
Para ser imutabilidade honesto como um conceito é muito mais clara quando se lida com tipos de referência, porque não é um objeto na pilha cujo estado interno nunca muda, por isso, quando uma operação / método retorna um novo objeto com estado diferente é meio óbvio (por exemplo, você pode fazer uma verificação de igualdade de referência objeto com Object.ReferenceEquals (Object, Object).
Para tipos de valores não há nenhum objeto na pilha, não é apenas o slot de memória que contém os bits que são o valor.
Ok, mas o que acontece com o operador de negação unário que é definido em BigInteger:
public static BigInteger operator -(BigInteger value)
{
value._sign = -value._sign;
return value;
}
aparece para quebrar o padrão imutabilidade e directamente transformar o objeto BigInteger. Então
b = -b;
realmente muda um BigInteger existente no local sem retornar um novo objeto.