Perché le strutture C # sono immutabili?

Ero solo curioso di sapere perché le strutture, le stringhe ecc. Sono immutabili? Qual è la ragione per renderli immutabili e il resto degli oggetti come mutabili. Quali sono le cose che sono considerate per rendere un object immutabile?

C’è qualche differenza sul modo in cui la memoria viene allocata e deallocata per oggetti mutevoli e immutabili?

Se questo argomento ti interessa, ho una serie di articoli sulla programmazione immutabile su http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/

Ero solo curioso di sapere perché le strutture, le stringhe ecc. Sono immutabili?

Le strutture e le classi non sono immutabili per impostazione predefinita, sebbene sia una buona pratica rendere le strutture immutabili. Mi piacciono anche le lezioni immutabili.

Le stringhe sono immutabili.

Qual è la ragione per renderli immutabili e il resto degli oggetti come mutabili.

Ragioni per rendere immutabili tutti i tipi:

  • È più facile ragionare sugli oggetti che non cambiano. Se ho una coda con tre elementi, so che non è vuota ora, non era vuota cinque minuti fa, non sarà vuota in futuro. È immutabile! Una volta che ne so qualcosa, posso usare questo fatto per sempre. I fatti riguardanti gli oggetti immutabili non diventano stantii.

  • Un caso speciale del primo punto: gli oggetti immutabili sono molto più facili da rendere sicuri. La maggior parte dei problemi di sicurezza dei thread sono dovuti alle scritture su un thread e alle letture su un altro; oggetti immutabili non hanno scritture.

  • Oggetti immutabili possono essere smontati e riutilizzati. Ad esempio, se si dispone di un albero binario immutabile, è ansible utilizzare i suoi sottoalberi sinistro e destro come sottoalberi di un albero diverso senza preoccuparsi di ciò. In una struttura mutevole, in genere si finisce per creare copie di dati per riutilizzarle perché non si desidera che le modifiche a un object logico interessino un altro. Questo può far risparmiare molto tempo e memoria.

Ragioni per rendere le strutture immutabili

Ci sono molti motivi per rendere le strutture immutabili. Eccone solo uno.

Le strutture vengono copiate per valore, non per riferimento. È facile trattare accidentalmente una struttura come copiata per riferimento. Per esempio:

 void M() { S s = whatever; ... lots of code ... s.Mutate(); ... lots more code ... Console.WriteLine(s.Foo); ... } 

Ora vuoi rifattorizzare parte di quel codice in un metodo di supporto:

 void Helper(S s) { ... lots of code ... s.Mutate(); ... lots more code ... } 

SBAGLIATO! Questo dovrebbe essere (ref S s) – se non lo fai allora la mutazione avverrà su una copia di s. Se non permetti le mutazioni in primo luogo, tutti questi problemi scompaiono.

Motivi per rendere le stringhe immutabili

Ricordi il mio primo punto sui fatti riguardanti le strutture immutabili che stanno in piedi?

Supponiamo che la stringa fosse mutabile:

 public static File OpenFile(string filename) { if (!HasPermission(filename)) throw new SecurityException(); return InternalOpenFile(filename); } 

Cosa succede se il chiamante ostile cambia il nome del file dopo il controllo di sicurezza e prima che il file venga aperto? Il codice ha appena aperto un file a cui potrebbero non essere autorizzati!

Di nuovo, i dati mutabili sono difficili da ragionare. Volete il fatto che “questo chiamante sia autorizzato a vedere il file descritto da questa stringa” sia vero per sempre , non fino a quando non si verifica una mutazione . Con stringhe mutabili, per scrivere codice sicuro dovremmo costantemente fare copie dei dati che sappiamo non cambiano.

Quali sono le cose che sono considerate per rendere un object immutabile?

Il tipo rappresenta logicamente qualcosa che è un valore “eterno”? Il numero 12 è il numero 12; non cambia. I numeri interi dovrebbero essere immutabili. Il punto (10, 30) è il punto (10, 30); non cambia. I punti dovrebbero essere immutabili. La stringa “abc” è la stringa “abc”; non cambia. Le stringhe dovrebbero essere immutabili. L’elenco (10, 20, 30) non cambia. E così via.

A volte il tipo rappresenta cose che cambiano. Il cognome di Mary Smith è Smith, ma domani potrebbe essere Mary Jones. O la signorina Smith oggi potrebbe essere il dottor Smith domani. L’alieno ha cinquanta punti salute ora ma ne ha dieci dopo essere stato colpito dal raggio laser. Alcune cose sono meglio rappresentate come mutazioni.

C’è qualche differenza sul modo in cui la memoria viene allocata e deallocata per oggetti mutevoli e immutabili?

Non come tale. Come ho già detto, una delle cose belle dei valori immutabili è che puoi riutilizzarne alcune parti senza fare copie. Quindi in questo senso, l’allocazione della memoria può essere molto diversa.

Struttura non … ecco perché le strutture mutevoli sono malvagie.

La creazione di strutture mutabili può portare a qualsiasi tipo di comportamento strano nell’applicazione e, pertanto, viene considerata una pessima idea (derivante dal fatto che assomigliano a un tipo di riferimento ma in realtà sono un tipo di valore e verranno copiati ogni volta che si passa loro intorno).

Le stringhe, d’altra parte, lo sono. Ciò li rende intrinsecamente thread-safe oltre a consentire ottimizzazioni tramite internamento di stringhe. Se è necessario build al volo una stringa complicata, è ansible utilizzare StringBuilder .

I concetti di mutabilità e immutabilità hanno significati diversi se applicati a strutture e classi. Un aspetto chiave (spesso, la debolezza principale) delle classi mutabili è se Foo ha un campo Bar di tipo List , che contiene un riferimento a una lista contenente (1,2,3), altro codice che ha un riferimento a lo stesso elenco potrebbe modificarlo, in modo tale che Bar contenga un riferimento a un elenco contenente (4,5,6), anche se quell’altro codice non ha alcun accesso a Bar . Al contrario, se Foo aveva un campo Biz di tipo System.Drawing.Point , l’unico modo in cui qualsiasi cosa poteva modificare qualsiasi aspetto di Biz sarebbe avere accesso in scrittura a quel campo .

I campi (pubblici e privati) di una struttura possono essere mutati da qualsiasi codice che può mutare la posizione di memoria in cui è memorizzata la struttura e non può essere mutato da alcun codice che non possa mutare la posizione di memorizzazione in cui è memorizzato. Se tutte le informazioni incapsulate all’interno di una struttura sono contenute nei suoi campi, una tale struttura può effettivamente combinare il controllo di un tipo immutabile con la convenienza di un tipo mutevole, a meno che la struttura non sia codificata in modo tale da rimuovere tale comodità ( un’abitudine che, purtroppo, alcuni programmatori Microsoft raccomandano).

Il “problema” con le strutture è che quando un metodo (inclusa un’implementazione di proprietà) viene invocato su una struttura in un contesto di sola lettura (o posizione immutabile), il sistema copia la struttura, esegue il metodo sulla copia temporanea e silenziosamente scarta il risultato. Questo comportamento ha portato i programmatori a mettere in discussione la sfortunata idea che il modo per evitare problemi con i metodi mutanti è di avere molte strutture che non consentono aggiornamenti a tratti, quando i problemi potevano essere meglio evitati semplicemente sostituendo le proprietà con i campi esposti .

Per inciso, alcune persone si lamentano che quando una proprietà di class restituisce una struttura opportunamente mutevole, le modifiche alla struttura non influenzano la class da cui provengono. Direi che è una buona cosa: il fatto che l’object restituito sia una struttura rende il comportamento chiaro (specialmente se si tratta di una struttura a campo scoperto). Confrontare uno snippet usando un’ipotetica struttura e proprietà su Drawing.Matrix con uno che utilizza una proprietà effettiva su quella class implementata da Microsoft:

 // Struttura ipotetica
 struttura pubblica {
   float pubblico xx, xy, yx, yy, dx, dy;
 } Transform2d;

 // Proprietà ipotetica di "System.Drawing.Drawing2d.Matrix"
 trasformazione pubblica Transform2d {get;}

 // Proprietà effettiva di "System.Drawing.Drawing2d.Matrix"
 public float [] Elements {get;  }

 // Codice che utilizza la struttura ipotetica
 Transform2d myTransform = myMatrix.Transform;
 myTransform.dx + = 20;
 ... altro codice che utilizza myTransform

 // Codice che utilizza la proprietà Microsoft effettiva
 float [] myArray = myMatrix.Elements;
 myArray [4] + = 20;
 ... altro codice che utilizza myArray

Osservando la reale proprietà di Microsoft, c’è un modo per dire se la scrittura su myArray[4] avrà effetto su myMatrix ? Anche guardando la pagina http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx c’è un modo per dirlo? Se la proprietà fosse stata scritta usando l’equivalente basato su struct, non ci sarebbe stata confusione; la proprietà che restituisce la struttura non restituirebbe nulla di più o di meno del valore attuale di sei numeri. Cambiare myTransform.dx sarebbe niente di più né di meno di una scrittura su una variabile a virgola mobile che non era collegata a nient’altro. Chiunque non myTransform.dx il fatto che cambiare myTransform.dx non abbia effetto su myMatrix dovrebbe essere ugualmente infastidito dal fatto che scrivere myArray[4] non abbia effetto su myMatrix , tranne che l’indipendenza di myMatrix e myTransform è evidente, mentre l’indipendenza di myMatrix e myArray non lo sono.

Un tipo di struttura non è immutabile. Sì, le stringhe sono. Rendere il proprio tipo immutabile è facile, semplicemente non fornire un costruttore predefinito, rendere tutti i campi privati ​​e non definire metodi o proprietà che modificano un valore di campo. Avere un metodo che dovrebbe mutare l’object restituisce invece un nuovo object. C’è un angolo di gestione della memoria, si tende a creare un sacco di copie e spazzatura.

Le strutture possono essere modificabili, ma è una ctriggers idea perché hanno una semantica della copia. Se apporti una modifica a una struttura, potresti effettivamente modificarne una copia. Tenere traccia di esattamente ciò che è stato cambiato è molto difficile.

Le strutture mutevoli generano errori.