¿Cuáles son sus reglas generales para cuándo usar estructuras vs. clases? Estoy pensando en la definición de C # de esos términos, pero si su lenguaje tiene conceptos similares, me gustaría escuchar su opinión también.
Tiendo a usar clases para casi todo, y uso estructuras solo cuando algo es muy simplista y debe ser un tipo de valor, como un número de teléfono o algo así. Pero esto parece un uso relativamente menor y espero que haya casos de uso más interesantes.
Respuestas:
La regla general a seguir es que las estructuras deben ser colecciones pequeñas, simples (de un nivel) de propiedades relacionadas, que son inmutables una vez creadas; para cualquier otra cosa, usa una clase.
C # es bueno porque las estructuras y clases no tienen diferencias explícitas en la declaración que no sea la palabra clave definitoria; por lo tanto, si siente que necesita "actualizar" una estructura a una clase, o viceversa "degradar" una clase a una estructura, es principalmente una cuestión de cambiar la palabra clave (hay algunas otras trampas; las estructuras no pueden derivar de cualquier otra clase o tipo de estructura, y no pueden definir explícitamente un constructor sin parámetros predeterminado).
Digo "sobre todo", porque lo más importante que se debe saber acerca de las estructuras es que, dado que son tipos de valor, tratarlas como clases (tipos de referencia) puede terminar siendo un dolor y medio. Particularmente, hacer que las propiedades de una estructura sean mutables puede causar un comportamiento inesperado.
Por ejemplo, supongamos que tiene una clase SimpleClass con dos propiedades, A y B. Instancia una copia de esta clase, inicializa A y B y luego pasa la instancia a otro método. Ese método modifica aún más A y B. De vuelta en la función de llamada (la que creó la instancia), A y B de su instancia tendrán los valores que les da el método llamado.
Ahora, hazlo una estructura. Las propiedades aún son mutables. Realiza las mismas operaciones con la misma sintaxis que antes, pero ahora, los nuevos valores de A y B no están en la instancia después de llamar al método. ¿Que pasó? Bueno, tu clase ahora es una estructura, lo que significa que es un tipo de valor. Si pasa un tipo de valor a un método, el valor predeterminado (sin una palabra clave out o ref) es pasar "por valor"; se crea una copia superficial de la instancia para su uso por el método, y luego se destruye cuando el método termina dejando la instancia inicial intacta.
Esto se vuelve aún más confuso si tuviera un tipo de referencia como miembro de su estructura (no está prohibido, pero es una práctica extremadamente mala en prácticamente todos los casos); la clase no se clonaría (solo la referencia de la estructura a la misma), por lo que los cambios en la estructura no afectarían al objeto original, pero los cambios en la subclase de la estructura AVERÍAN la instancia del código de llamada. Esto puede poner fácilmente estructuras mutables en estados muy inconsistentes que pueden causar errores muy lejos de donde está el problema real.
Por esta razón, prácticamente todas las autoridades en C # dicen que siempre haga que sus estructuras sean inmutables; permitir al consumidor especificar los valores de las propiedades solo en la construcción de un objeto, y nunca proporcionar ningún medio para cambiar los valores de esa instancia. Los campos de solo lectura, o propiedades de solo obtención, son la regla. Si el consumidor quiere cambiar el valor, puede crear un nuevo objeto basado en los valores del anterior, con los cambios que desee, o puede llamar a un método que hará lo mismo. Esto los obliga a tratar una sola instancia de su estructura como un "valor" conceptual, indivisible y distinto (pero posiblemente equiparable) de todos los demás. Si realizan una operación en un "valor" almacenado por su tipo, obtienen un nuevo "valor" que es diferente de su valor inicial,
Para un buen ejemplo, mira el tipo DateTime. No puede asignar ninguno de los campos de una instancia de DateTime directamente; debe crear uno nuevo o llamar a un método en el existente que producirá una nueva instancia. Esto se debe a que una fecha y hora son un "valor", como el número 5, y un cambio en el número 5 da como resultado un nuevo valor que no es 5. Solo porque 5 + 1 = 6 no significa que 5 es ahora 6 porque le agregaste 1. DateTimes funciona de la misma manera; 12:00 no se "convierte" en 12:01 si agrega un minuto, en su lugar obtiene un nuevo valor 12:01 que es distinto de 12:00. Si este es un estado de cosas lógico para su tipo (buenos ejemplos conceptuales que no están integrados en .NET son Money, Distance, Weight y otras cantidades de una UOM donde las operaciones deben tener en cuenta todas las partes del valor) luego use una estructura y diseñe en consecuencia. En la mayoría de los otros casos en los que los subelementos de un objeto deben ser mutables independientemente, use una clase.
fuente
La respuesta: "Usar una estructura para construcciones de datos puros y una clase para objetos con operaciones" es definitivamente una OMI incorrecta. Si una estructura tiene una gran cantidad de propiedades, entonces una clase es casi siempre más apropiada. Microsoft a menudo dice, desde un punto de vista de eficiencia, si su tipo es mayor de 16 bytes, debería ser una clase.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
fuente
La diferencia más importante entre ay
class
astruct
es lo que sucede en la siguiente situación:¿Cuál debería ser el efecto de la última declaración
thing1.somePropertyOrField
? SiThing
una estructura, ysomePropertyOrField
es un campo público expuesto, los objetosthing1
ything2
serán "separados" entre sí, por lo que la última declaración no afectaráthing1
. SiThing
es una clase, entoncesthing1
y sething2
unirán entre sí, por lo que se escribirá la última declaraciónthing1.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:
¿La segunda declaración alterará la información almacenada
myPeople
? SiPerson
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 ; siPerson
es una estructura y uno desea actualizarmyPeople
, claramente tendría que hacer algo como esomyPeople.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 contenidoMyPeople
, 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
yExchange
, y suponga que uno tiene unList<PhoneNumber>
. El efecto de lo siguiente debería ser bastante claro:Tenga en cuenta que nada en el código anterior sabe o no le importa ningún campo que
PhoneNumber
no seaAreaCode
yExchange
. SiPhoneNumber
fuera una estructura llamada "inmutable", sería necesario que proporcionara unwithXX
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.
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á.
fuente
Tiendo a usar estructuras solo cuando se necesita un método, por lo que una clase parece demasiado "pesada". Así, a veces trato las estructuras como objetos ligeros. CUIDADO: esto no siempre funciona y no siempre es lo mejor, ¡así que realmente depende de la situación!
RECURSOS ADICIONALES
Para una explicación más formal, MSDN dice: http://msdn.microsoft.com/en-us/library/ms229017.aspx Este enlace verifica lo que mencioné anteriormente, las estructuras solo deben usarse para alojar una pequeña cantidad de datos.
fuente
Use una estructura para construcciones de datos puros y una clase para objetos con operaciones.
Si su estructura de datos no necesita control de acceso y no tiene operaciones especiales que no sean get / set, use una estructura. Esto hace obvio que toda esa estructura es un contenedor de datos.
Si su estructura tiene operaciones que modifican la estructura de una manera más compleja, use una clase. Una clase también puede interactuar con otras clases a través de argumentos a métodos.
Piense en ello como la diferencia entre un ladrillo y un avión. Un ladrillo es una estructura; Tiene longitud, anchura y altura. No puede hacer mucho Un avión podría tener múltiples operaciones que lo mutan de alguna manera, como mover las superficies de control y cambiar el empuje del motor. Sería mejor como clase.
fuente