La differenza più importante tra a class
e a struct
è ciò che accade nella seguente situazione:
Thing thing1 = new Thing ();
thing1.somePropertyOrField = 5;
Thing thing2 = thing1;
thing2.somePropertyOrField = 9;
Quale dovrebbe essere l'effetto dell'ultima affermazione su thing1.somePropertyOrField
? Se Thing
una struttura, ed somePropertyOrField
è un campo pubblico esposto, gli oggetti thing1
e thing2
saranno "staccati" l'uno dall'altro, quindi quest'ultima affermazione non avrà alcun effetto thing1
. Se Thing
è una classe, allora thing1
e thing2
sarà collegata l'una all'altra, e quindi quest'ultima istruzione scriverà thing1.somePropertyOrField
. Si dovrebbe usare una struttura nei casi in cui la prima semantica avrebbe più senso, e si dovrebbe usare una classe nei casi in cui la seconda semantica avrebbe più senso.
Nota che mentre alcune persone consigliano che il desiderio di rendere qualcosa di mutevole è un argomento a favore del fatto che sia la classe, suggerirei che è vero il contrario: se qualcosa che esiste allo scopo di conservare alcuni dati sarà mutabile, e se non sarà ovvio se le istanze sono associate a qualcosa, la cosa dovrebbe essere una struttura (probabilmente con campi esposti) in modo da chiarire che le istanze non sono collegate a nient'altro.
Ad esempio, considera le dichiarazioni:
Persona somePerson = myPeople.GetPerson ("123-45-6789");
somePerson.Name = "Mary Johnson"; // Era stato "Mary Smith"
La seconda affermazione modificherà le informazioni archiviate myPeople
? Se Person
è una struttura a campo esposto, non lo farà, e il fatto che non lo sarà sarà una conseguenza ovvia del suo essere una struttura a campo esposto ; se Person
è una struttura e si desidera aggiornare myPeople
, si dovrebbe chiaramente fare qualcosa di simile myPeople.UpdatePerson("123-45-6789", somePerson)
. Se Person
è una classe, tuttavia, potrebbe essere molto più difficile determinare se il codice sopra non aggiornerà mai il contenuto MyPeople
, lo aggiornerà sempre o talvolta lo aggiornerà.
Per quanto riguarda l'idea che le strutture debbano essere "immutabili", non sono d'accordo in generale. Esistono casi di utilizzo validi per strutture "immutabili" (in cui gli invarianti sono fatti valere in un costruttore) ma richiedono che una intera struttura debba essere riscritta ogni volta che una parte di essa cambia è scomoda, dispendiosa e più adatta a causare bug rispetto alla semplice esposizione campi direttamente. Ad esempio, considera una PhoneNumber
struttura i cui campi includono, tra gli altri, AreaCode
e Exchange
, e supponiamo che uno abbia un List<PhoneNumber>
. L'effetto di quanto segue dovrebbe essere abbastanza chiaro:
per (int i = 0; i <myList.Count; i ++)
{
PhoneNumber theNumber = myList [i];
if (theNumber.AreaCode == "312")
{
stringa newExchange = "";
if (new312to708Exchanges.TryGetValue (theNumber.Exchange), out newExchange)
{
theNumber.AreaCode = "708";
theNumber.Exchange = newExchange;
myList [i] = theNumber;
}
}
}
Si noti che nulla nel codice sopra è a conoscenza o si preoccupa per qualsiasi campo PhoneNumber
diverso da AreaCode
e Exchange
. Se PhoneNumber
fosse una cosiddetta struttura "immutabile", sarebbe necessario che fornisse un withXX
metodo per ciascun campo, che restituirebbe una nuova istanza di struttura che contenesse il valore passato nel campo indicato, altrimenti sarebbe necessario per il codice come sopra per conoscere ogni campo nella struttura. Non proprio attraente.
A proposito, ci sono almeno due casi in cui le strutture possono ragionevolmente contenere riferimenti a tipi mutabili.
- La semantica della struttura indica che sta memorizzando l'identità dell'oggetto in questione, piuttosto che come scorciatoia per contenere le proprietà dell'oggetto. Ad esempio, un `KeyValuePair` conterrebbe le identità di alcuni pulsanti, ma non ci si aspetterebbe che contenga informazioni persistenti sulle posizioni di tali pulsanti, gli stati di evidenziazione, ecc.
- La struttura sa che contiene l'unico riferimento a quell'oggetto, nessun altro otterrà un riferimento e qualsiasi mutazione che verrà mai eseguita su quell'oggetto sarà stata fatta prima che un riferimento ad esso venga archiviato ovunque.
Nel primo scenario, l'IDENTITÀ dell'oggetto sarà immutabile; nel secondo, la classe dell'oggetto nidificato potrebbe non imporre l'immutabilità, ma la struttura che detiene il riferimento lo farà.