La diferencia más importante entre ay class
a struct
es lo que sucede en la siguiente situación:
Cosa thing1 = nueva Cosa ();
thing1.somePropertyOrField = 5;
Cosa thing2 = thing1;
thing2.somePropertyOrField = 9;
¿Cuál debería ser el efecto de la última declaración thing1.somePropertyOrField
? Si Thing
una estructura, y somePropertyOrField
es un campo público expuesto, los objetos thing1
y thing2
serán "separados" entre sí, por lo que la última declaración no afectará thing1
. Si Thing
es una clase, entonces thing1
y se thing2
unirán entre sí, por lo que se escribirá la última declaración thing1.somePropertyOrField
. Uno debería usar una estructura en los casos en que la semántica anterior tendría más sentido, y debería usar una clase en los casos en que la última semántica tuviera más sentido.
Tenga en cuenta que si bien algunas personas aconsejan que el deseo de hacer algo mutable es un argumento a favor de que sea la clase, sugeriría lo contrario: si algo que existe con el propósito de mantener algunos datos va a ser mutable, y Si no será obvio si las instancias están unidas a algo, la cosa debería ser una estructura (probablemente con campos expuestos) para dejar en claro que las instancias no están unidas a nada más.
Por ejemplo, considere las declaraciones:
Persona somePerson = myPeople.GetPerson ("123-45-6789");
somePerson.Name = "Mary Johnson"; // Había sido "Mary Smith"
¿La segunda declaración alterará la información almacenada myPeople
? Si Person
es una estructura de campo expuesto, no lo será, y el hecho de que no lo sea será una consecuencia obvia de ser una estructura de campo expuesto ; si Person
es una estructura y uno desea actualizar myPeople
, claramente tendría que hacer algo como eso myPeople.UpdatePerson("123-45-6789", somePerson)
. Person
Sin embargo, si es una clase, puede ser mucho más difícil determinar si el código anterior nunca actualizará el contenido MyPeople
, siempre lo actualizará o, a veces, lo actualizará.
Con respecto a la noción de que las estructuras deben ser "inmutables", no estoy de acuerdo en general. Hay casos de uso válidos para estructuras "inmutables" (donde los invariantes se imponen en un constructor) pero requieren que una estructura completa se reescriba cada vez que cualquier parte de ella cambie es incómoda, derrochadora y más propensa a causar errores de lo que simplemente expondría campos directamente. Por ejemplo, considere una PhoneNumber
estructura cuyos campos, incluir entre otros, AreaCode
y Exchange
, y suponga que uno tiene un List<PhoneNumber>
. El efecto de lo siguiente debería ser bastante claro:
para (int i = 0; i <myList.Count; i ++)
{
PhoneNumber theNumber = myList [i];
if (theNumber.AreaCode == "312")
{
cadena newExchange = "";
if (new312to708Exchanges. TryGetValue (theNumber.Exchange), out newExchange)
{
theNumber.AreaCode = "708";
theNumber.Exchange = newExchange;
myList [i] = theNumber;
}
}
}
Tenga en cuenta que nada en el código anterior sabe o no le importa ningún campo que PhoneNumber
no sea AreaCode
y Exchange
. Si PhoneNumber
fuera una estructura llamada "inmutable", sería necesario que proporcionara un withXX
método para cada campo, que devolvería una nueva instancia de estructura que contenía el valor pasado en el campo indicado, o de lo contrario sería necesario para código como el anterior para saber acerca de cada campo en la estructura. No exactamente atractivo.
Por cierto, hay al menos dos casos en los que las estructuras pueden contener referencias a tipos mutables.
- La semántica de la estructura indica que está almacenando la identidad del objeto en cuestión, en lugar de como un atajo para mantener las propiedades del objeto. Por ejemplo, un `KeyValuePair` mantendría las identidades de ciertos botones, pero no se esperaría que contuviera una información persistente sobre las posiciones de esos botones, los estados resaltados, etc.
- La estructura sabe que tiene la única referencia a ese objeto, nadie más obtendrá una referencia, y cualquier mutación que alguna vez se realice a ese objeto se habrá realizado antes de que se almacene una referencia a él en cualquier lugar.
En el primer escenario, la IDENTIDAD del objeto será inmutable; en el segundo, la clase del objeto anidado puede no imponer la inmutabilidad, pero la estructura que contiene la referencia sí lo hará.