.net - programacion - ¿Cuándo debo usar una estructura en lugar de una clase?
estructura de una clase en programacion (14)
Además las excelentes respuestas anteriores:
Las estructuras son tipos de valor.
Nunca se pueden establecer en Nada .
Establecer una estructura = Nada, establecerá todos sus tipos de valores a sus valores predeterminados.
MSDN dice que debes usar estructuras cuando necesites objetos livianos. ¿Hay otros escenarios cuando una estructura es preferible a una clase?
Algunas personas podrían haber olvidado que:
- Las estructuras pueden tener métodos.
- Las estructuras no pueden ser heredadas.
Entiendo las diferencias técnicas entre estructuras y clases, simplemente no tengo una idea clara de cuándo usar una estructura.
Bill Wagner tiene un capítulo sobre esto en su libro "c # efectivo" ( http://www.amazon.com/Effective-Specific-Ways-Improve-Your/dp/0321245660 ). Concluye utilizando el siguiente principio:
- ¿Es la principal responsabilidad del almacenamiento de datos de tipo?
- ¿Está su interfaz pública definida completamente por las propiedades que acceden o modifican sus miembros de datos?
- ¿Estás seguro de que tu tipo nunca tendrá subclases?
- ¿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, utilice una clase.
Como dijo @Simon, las estructuras proporcionan semántica de "tipo de valor" por lo que si necesita un comportamiento similar al de un tipo de datos incorporado, use una estructura. Dado que las estructuras se pasan por copia, debe asegurarse de que sean pequeñas en tamaño, alrededor de 16 bytes.
Creo que la mejor respuesta es simplemente usar struct cuando lo que necesita es una colección de propiedades, clase cuando se trata de una colección de propiedades Y comportamientos.
Hmm ...
No usaría la recolección de basura como un argumento a favor / en contra del uso de estructuras vs clases. El montón administrado funciona de manera muy parecida a una pila: crear un objeto simplemente lo coloca en la parte superior del montón, que es casi tan rápido como asignar en la pila. Además, si un objeto es de corta duración y no sobrevive a un ciclo de 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 perezoso para ir a buscarlos).
La mayoría de las veces utilizo una estructura, termino dándome una patada por hacerlo, porque más tarde descubro que tener una semántica de referencia habría simplificado un poco las cosas.
De todos modos, esos cuatro puntos en el artículo de MSDN publicado arriba parecen una buena guía.
Las estructuras están en la pila, no en el montón, por lo tanto, son seguras para subprocesos, y deben usarse al implementar el patrón de objetos de transferencia, nunca querrá usar objetos en el montón, son volátiles, en este caso desea utilizar la pila de llamadas, Este es un caso básico para usar una estructura. Estoy sorprendido por todas las respuestas aquí,
MSDN tiene la respuesta: elegir entre clases y estructuras .
Básicamente, esa página le proporciona 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 solo valor, similar a los tipos primitivos (entero, doble, etc.).
- Tiene un tamaño de instancia inferior a 16 bytes.
- Es inmutable.
- No tendrá que ser boxeado con frecuencia.
Me sorprende que no haya leído ninguna de las respuestas anteriores a esta, que considero el aspecto más crucial:
Uso structs 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 memoria o dos. Solo te preocupas por los valores que tienen.
Si una entidad va a ser inmutable, la cuestión de si usar una estructura o una clase generalmente será de desempeño 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 la 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 lugar entre ellas. 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án 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, del código creado una instancia de objeto y luego copió una referencia a las 500 ranuras de matriz, el costo total sería de 24 bytes para esa instancia y 4,000 para la matriz, un total de 4,024 bytes. Un gran ahorro. Pocas situaciones funcionarán tan bien como la última, pero en algunos casos puede ser posible copiar algunas referencias a suficientes ranuras de matriz 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 en cierto modo 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:
Thing t1,t2; ... t2 = t1; t2.x = 5;
¿Se quiere que la última afirmación 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 afirmación 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 declaración no afectará a t1.x.
Las estructuras mutables y las clases mutables tienen comportamientos fundamentalmente diferentes, aunque .net tiene algunas peculiaridades en el manejo de las mutaciones de estructura. Si uno quiere un comportamiento de tipo de valor (lo que significa que "t2 = t1" copiará los datos de t1 a t2 mientras deja t1 y t2 como casos distintos), y si puede vivir con las peculiaridades en el manejo de tipos de valor de .net, use una estructura. Si uno quiere una semántica de tipo de valor, pero las peculiaridades de .net causan una semántica de tipo de valor rota en su aplicación, use una clase y mumble.
Siempre he usado una estructura cuando quería agrupar algunos valores para pasar cosas de una llamada de método, pero no necesitaré usarla para nada después de haber leído esos valores. Solo como una manera 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"
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 ver preguntas anteriores, por ejemplo,
Yo usaría structs cuando:
se supone que un objeto debe ser de solo lectura (cada vez que se pasa / asigna una estructura, se copia). Los objetos de solo lectura son excelentes cuando se trata de un procesamiento multiproceso, ya que en la mayoría de los casos no requieren bloqueo.
Un objeto es pequeño y de corta vida. En tal caso, existe una buena posibilidad de que el objeto se asigne en la pila, 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 alcance. En otras palabras, es menos trabajo para el recolector de basura y la memoria se utiliza de manera más eficiente.
cuando realmente no necesita comportamiento, pero necesita más estructura que una simple matriz o diccionario.
Seguimiento Así es como pienso de las estructuras en general. Sé que pueden tener métodos, pero me gusta mantener esa distinción mental general.
Usa una clase si:
- Su identidad es importante. Las estructuras se copian implícitamente cuando se pasan por valor a un método.
- Tendrá una gran huella de memoria.
- Sus campos necesitan inicializadores.
- Necesitas heredar de una clase base.
- Necesitas un comportamiento polimórfico;
Use 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 P / Invoke que requiere que una estructura se pase 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 en valores no predeterminados.
- No es necesario que herede de una clase base (que no sea ValueType, de la que se heredan todas las estructuras).
- No necesitas un comportamiento polimórfico.