¿Cuándo debería usar una estructura en lugar de una clase?

MSDN dice que debe usar estructuras cuando necesite objetos livianos. ¿Hay otros escenarios cuando una estructura es preferible a una clase?

Algunas personas pueden haber olvidado eso:

  1. las estructuras pueden tener métodos.
  2. las estructuras no pueden ser heredadas

Entiendo las diferencias técnicas entre las estructuras y las clases, simplemente no tengo una buena idea de cuándo usar una estructura.

MSDN tiene la respuesta: elegir entre clases y estructuras .

Básicamente, esa página le da una lista de verificación de 4 elementos y dice que use una clase a menos que su tipo cumpla con todos los criterios.

No defina una estructura a menos que el tipo tenga todas las características siguientes:

  • Lógicamente representa un único valor, similar a los tipos primitivos (entero, doble, etc.).
  • Tiene un tamaño de instancia inferior a 16 bytes.
  • Es inmutable
  • No será necesario encasillarlo frecuentemente.

Me sorprende que no haya leído en ninguna de las respuestas anteriores esto, que considero el aspecto más crucial:

Yo uso estructuras cuando quiero un tipo sin identidad. Por ejemplo, un punto 3D:

 public struct ThreeDimensionalPoint { public readonly int X, Y, Z; public ThreeDimensionalPoint(int x, int y, int z) { this.X = x; this.Y = y; this.Z = z; } public override string ToString() { return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")"; } public override int GetHashCode() { return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2); } public override bool Equals(object obj) { if (!(obj is ThreeDimensionalPoint)) return false; ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj; return this == other; } public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z; } public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return !(p1 == p2); } } 

Si tiene dos instancias de esta estructura, no le importa si son una sola pieza de datos en la memoria o dos. Solo te importan los valores que tienen.

Bill Wagner tiene un capítulo sobre esto en su libro “effective c #” ( http://www.amazon.com/Effective-Specific-Ways-Improve-Your/dp/0321245660 ). Concluye usando el siguiente principio:

  1. ¿Es la principal responsabilidad del almacenamiento de datos tipo?
  2. ¿Su interfaz pública está definida por completo por propiedades que acceden o modifican sus miembros de datos?
  3. ¿Estás seguro de que tu tipo nunca tendrá subclases?
  4. ¿Estás seguro de que tu tipo nunca será tratado polimórficamente?

Si responde “sí” a las 4 preguntas: use una estructura. De lo contrario, use una clase.

Use una estructura cuando desee semántica de tipo valor en lugar de tipo de referencia. Las estructuras son copia por valor, ¡así que ten cuidado!

También vea las preguntas anteriores, por ej.

¿Cuál es la diferencia entre struct y clase en .NET?

Yo usaría estructuras cuando:

  1. se supone que un objeto es de solo lectura (cada vez que pasa / asigna una estructura, se copia). Los objetos de solo lectura son excelentes cuando se trata de procesamiento multiproceso ya que no requieren locking en la mayoría de los casos.

  2. un objeto es pequeño y de vida corta. En tal caso, existe una buena posibilidad de que el objeto se asigne en la stack, lo que es mucho más eficiente que colocarlo en el montón administrado. Además, la memoria asignada por el objeto se liberará tan pronto como salga de su scope. En otras palabras, es menos trabajo para Garbage Collector y la memoria se usa de manera más eficiente.

Usa una clase si:

  • Su identidad es importante Las estructuras se copian implícitamente cuando se pasan por valor en un método.
  • Tendrá una gran huella de memoria.
  • Sus campos necesitan inicializadores.
  • Necesitas heredar de una clase base.
  • Necesitas comportamiento polimórfico;

Usa una estructura si:

  • Actuará como un tipo primitivo (int, long, byte, etc.).
  • Debe tener una pequeña huella de memoria.
  • Está llamando a un método de P / Invoke que requiere que se pase una estructura por valor.
  • Debe reducir el impacto de la recolección de basura en el rendimiento de la aplicación.
  • Sus campos deben inicializarse solo a sus valores predeterminados. Este valor sería cero para los tipos numéricos, falso para los tipos booleanos y nulo para los tipos de referencia.
    • Tenga en cuenta que en C # 6.0 las estructuras pueden tener un constructor predeterminado que se puede usar para inicializar los campos de la estructura a valores no predeterminados.
  • No necesita heredar de una clase base (que no sea ValueType, de la cual heredan todas las estructuras).
  • No necesitas comportamiento polimórfico.

Siempre utilicé una estructura cuando quería agrupar algunos valores para pasar cosas de una llamada a método, pero no necesitaré usarla para nada después de haber leído esos valores. Solo como una forma de mantener las cosas limpias. Tiendo a ver las cosas en una estructura como “desechables” y las cosas en una clase como más útiles y “funcionales”

Si una entidad va a ser inmutable, la cuestión de si usar una estructura o una clase generalmente será de rendimiento en lugar de semántica. En un sistema de 32/64 bits, las referencias de clase requieren 4/8 bytes para almacenar, independientemente de la cantidad de información en la clase; copiar una referencia de clase requerirá copiar 4/8 bytes. Por otro lado, cada instancia de clase distinta tendrá 8/16 bytes de sobrecarga además de la información que contiene y el costo de memoria de las referencias a ella. Supongamos que uno quiere una matriz de 500 entidades, cada una con cuatro enteros de 32 bits. Si la entidad es un tipo de estructura, la matriz requerirá 8,000 bytes independientemente de si las 500 entidades son todas idénticas, todas diferentes o en algún punto intermedio. Si la entidad es un tipo de clase, la matriz de 500 referencias tomará 4.000 bytes. Si todas esas referencias apuntan a objetos diferentes, los objetos requerirían 24 bytes adicionales cada uno (12,000 bytes para los 500), un total de 16,000 bytes, el doble del costo de almacenamiento de un tipo de estructura. Por otro lado, si el código creó una instancia de objeto y luego copió una referencia a las 500 ranuras de la matriz, el costo total sería de 24 bytes para esa instancia y de 4.000 para la matriz, un total de 4.024 bytes. Un gran ahorro. Pocas situaciones funcionarían tan bien como la última, pero en algunos casos podría ser posible copiar algunas referencias a suficientes ranuras de arreglos para que valga la pena compartirlas.

Si se supone que la entidad es mutable, la cuestión de si usar una clase o estructura es de alguna manera más fácil. Supongamos que “cosa” es una estructura o clase que tiene un campo entero llamado x, y uno hace el siguiente código:

   Cosa t1, t2;
   ...
   t2 = t1;
   t2.x = 5;

¿Desea que la última statement afecte a t1.x?

Si Thing es un tipo de clase, t1 y t2 serán equivalentes, lo que significa que t1.x y t2.x también serán equivalentes. Por lo tanto, la segunda statement afectará a t1.x. Si Thing es un tipo de estructura, t1 y t2 serán instancias diferentes, lo que significa que t1.x y t2.x se referirán a diferentes enteros. Por lo tanto, la segunda statement no afectará a t1.x.

Las estructuras mutables y las clases mutables tienen comportamientos fundamentalmente diferentes, aunque .net tiene algunos caprichos en el manejo de las mutaciones estructurales. Si uno quiere un comportamiento de tipo valor (lo que significa que “t2 = t1” copiará los datos de t1 a t2 dejando t1 y t2 como instancias distintas), y si uno puede vivir con las peculiaridades en el manejo de tipos de valores de .net, use una estructura. Si uno quiere semántica de tipo de valor pero los caprichos de .net causarían una semántica de tipo de valor roto en la aplicación, use una clase y murmure.

Además, las excelentes respuestas anteriores:

Las estructuras son tipos de valores.

Nunca se pueden configurar a Nada .

Establecer una estructura = Nada, establecerá todos sus tipos de valores a sus valores predeterminados.

cuando realmente no necesitas comportamiento, pero necesitas más estructura que una simple matriz o diccionario.

Seguimiento Así es como pienso en las estructuras en general. Sé que pueden tener métodos, pero me gusta mantener esa distinción mental general.

Como dijo @Simon, las estructuras proporcionan una semántica de “valor-tipo”, por lo que si necesita un comportamiento similar al de un tipo de datos integrado, utilice una estructura. Como las estructuras pasan por copia, debes asegurarte de que sean de pequeño tamaño, alrededor de 16 bytes.

Hmm …

No utilizaría la recolección de elementos no utilizados como argumento a favor / en contra del uso de estructuras frente a clases. El montón gestionado funciona de manera similar a una stack: la creación de un objeto simplemente lo coloca en la parte superior del montón, que es casi tan rápido como la asignación en la stack. Además, si un objeto es efímero y no sobrevive a un ciclo GC, la desasignación es gratuita, ya que el GC solo funciona con la memoria que todavía está accesible. (Busque en MSDN, hay una serie de artículos sobre administración de memoria .NET, soy demasiado flojo para buscarlos).

La mayoría de las veces que utilizo una estructura, termino pateándome a mí mismo por hacerlo, porque más tarde descubro que tener semántica de referencia habría hecho las cosas un poco más simples.

De todos modos, esos cuatro puntos en el artículo de MSDN publicado anteriormente parece una buena guía.

Las estructuras están en la stack, no en el montón, por lo tanto, son seguras para hilos, y deben usarse cuando se implementa el patrón de objetos de transferencia; nunca se desean usar objetos en el montón. Son volátiles; en este caso, se debe usar la stack de llamadas. este es un caso básico para usar una estructura. Estoy sorprendido por todas las respuestas aquí,

Creo que la mejor respuesta es simplemente usar struct cuando lo que necesitas es una colección de propiedades, clase cuando se trata de una colección de propiedades AND behaviors.