Domanda

In .NET 4 beta 2, c'è il nuovo spazio dei nomi Numerics con struct BigInteger . La documentazione afferma che è un tipo immutabile, come mi sarei aspettato.

Ma sono un po 'confuso dall'operatore post-incremento ( ++ ). Questo sembra indubbiamente mutare il valore. Il seguente ciclo while funziona:

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

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

Questo è ciò che MSDN ha da dire sull'operatore Increment:

  

Perché gli oggetti BigInteger lo sono   immutabile, l'operatore Increment   crea un nuovo oggetto BigInteger di cui   il valore è uno in più rispetto al BigInteger   oggetto rappresentato da valore.   Pertanto, ripetute chiamate all'incremento   potrebbe essere costoso.

Va ??bene, avrei capito se avessi dovuto usare b = b ++ ma apparentemente ++ da solo è sufficiente per cambiare un valore.

Qualche idea?

È stato utile?

Soluzione

Gli operatori ++ e - sono implementati in termini dei normali operatori + e - , così in realtà:

b++;

è equivalente a:

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

Ora, come commentato, potrebbe sembrare che rompa l'immutabilità, ma non lo è.

Dovresti invece guardare questo codice nel modo seguente:

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

Questo lascerà in memoria due oggetti, il valore originale di BigInteger e il nuovo, ora indicato da b. Puoi facilmente verificare che questo sia ciò che accade con il seguente codice:

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

Quindi l'oggetto originale non è cambiato, quindi non rompe l'immutabilità, e per rispondere alla nuova parte della domanda, questo dovrebbe essere thread-safe.

Questa è la stessa cosa che succede alle stringhe:

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

Altri suggerimenti

Poiché BigInteger è immutabile, b ++ sarà equivalente a:

BigInteger temp=b;
b=temp+1;

Dopo questa operazione, la temperatura viene riciclata dal GC e la memoria viene liberata.

BigInteger b = BigInteger.One;

b++;  // immutable ?

Nel tuo esempio b è una variabile, che è solo uno slot di memoria nel frame dello stack del metodo corrente. Viene inizializzato su One e b ++ accetta b, crea un nuovo BigInteger (con il valore incrementato) e lo restituisce. la variabile b ora ha lo stato dal nuovo BigInteger restituito.

Ad essere sincero, l'immutabilità come concetto è molto più chiara quando si tratta di tipi di riferimento, perché c'è un oggetto nell'heap il cui stato interno non cambia mai, quindi quando un'operazione / metodo restituisce un nuovo oggetto con stato diverso è in qualche modo ovvio (ad es. è possibile eseguire un controllo dell'uguaglianza di riferimento dell'oggetto con object.ReferenceEquals (oggetto, oggetto).

Per i tipi di valore non c'è alcun oggetto nell'heap, c'è solo lo slot in memoria che contiene i bit che sono il valore.

Ok, ma per quanto riguarda l'operatore di negazione unaria che è definito su BigInteger:

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

sembra rompere il modello di immutabilità e mutare direttamente l'oggetto BigInteger. Quindi

b = -b;

modifica effettivamente un BigInteger esistente in posizione senza restituire un nuovo oggetto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top