Pregunta

En .NET 4 beta 2, existe el nuevo espacio de nombres Numerics con struct BigInteger . La documentación establece que Es un tipo inmutable, como hubiera esperado.

Pero estoy un poco confundido por el operador posterior al incremento ( ++ ). Esto definitivamente parece mutar el valor. El siguiente ciclo while funciona:

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

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

Esto es lo que MSDN tiene que decir sobre el operador Incremento:

  

Porque los objetos BigInteger son   inmutable, el operador Incremento   crea un nuevo objeto BigInteger cuyo   el valor es uno más que el BigInteger   objeto representado por valor.   Por lo tanto, repetidas llamadas al incremento   puede ser costoso.

Muy bien, habría entendido si tuviera que usar b = b ++ pero aparentemente ++ por sí solo es suficiente para cambiar un valor.

¿Alguna idea?

¿Fue útil?

Solución

Los operadores ++ y - se implementan en términos de los operadores normales + y - , así que en realidad:

b++;

es equivalente a:

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

Ahora, como se comentó, esto puede parecer que rompe la inmutabilidad, pero no lo hace.

En su lugar, debería mirar este código como haciendo esto:

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

Esto dejará dos objetos en la memoria, el valor BigInteger original y el nuevo, ahora referenciado por b. Puede verificar fácilmente que esto es lo que sucede con el siguiente código:

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

Entonces, el objeto original no cambió, por lo tanto, no rompe la inmutabilidad, y para responder la nueva parte de la pregunta, esto debería ser seguro para subprocesos.

Esto es lo mismo que le sucede a las cadenas:

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

Otros consejos

Dado que BigInteger es inmutable, b ++ será equivalente a:

BigInteger temp=b;
b=temp+1;

Después de esta operación, el GC recicla la temperatura y libera la memoria.

BigInteger b = BigInteger.One;

b++;  // immutable ?

En su ejemplo, b es una variable, que es solo una ranura de memoria en el marco de pila del método actual. Se inicializa en Uno, y b ++ toma b, crea un nuevo BigInteger (con el valor incrementado) y lo devuelve. La variable b ahora tiene el estado del nuevo BigInteger devuelto.

Para ser honesto, la inmutabilidad como concepto es mucho más clara cuando se trata de tipos de referencia, porque hay un objeto en el montón cuyo estado interno nunca cambia, por lo que cuando una operación / método devuelve un nuevo objeto con un estado diferente, es obvio (por ejemplo, puede hacer una comprobación de igualdad de referencia de objeto con object.ReferenceEquals (objeto, objeto).

Para los tipos de valor no hay ningún objeto en el montón, solo hay una ranura en la memoria que contiene los bits que son el valor.

Ok, pero ¿qué pasa con el operador de negación unario que se define en BigInteger:

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

parece romper el patrón de inmutabilidad y mutar directamente el objeto BigInteger. Entonces

b = -b;

en realidad cambia un BigInteger existente en su lugar sin devolver un nuevo objeto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top