La differenza più importante tra a classe 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 Thinguna struttura, ed somePropertyOrFieldè un campo pubblico esposto, gli oggetti thing1e thing2saranno "staccati" l'uno dall'altro, quindi quest'ultima affermazione non avrà alcun effetto thing1. Se Thingè una classe, allora thing1e thing2sarà 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 PhoneNumberstruttura i cui campi includono, tra gli altri, AreaCodee 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 PhoneNumberdiverso da AreaCodee Exchange. Se PhoneNumberfosse una cosiddetta struttura "immutabile", sarebbe necessario che fornisse un withXXmetodo 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à.