¿Cuál es mejor para el almacén de datos Struct / Classes?

Hemos visto mucha discusión en SO con respecto a la clase vs struct en c #. En su mayoría terminaron con conclusiones diciendo que es una asignación de memoria de stack / stack . Y recomendar el uso de estructuras en estructuras de datos pequeñas .

Ahora tengo una situación para decidir el almacén de datos simple entre estas dos opciones. Currenlty en nuestra aplicación tenemos miles de clases, solo actúa como almacenes de datos simples (solo campos públicos expuestos) y pasaron entre diferentes módulos y servicios.

Según mi comprensión, sentí que es mejor avanzar con struct en lugar de clases por razones de rendimiento. Debido a que estas son estructuras de datos simples, solo actúan como almacenes de datos.

Antes de continuar con esto, necesito un consejo experto de las personas que han experimentado esta lucha.

  • es mi entendimiento correcto?
  • He visto que la mayoría de los ORM tienen clases como almacenes de datos. Así que dudo que haya una razón para seguir adelante con las clases en lugar de las estructuras. ¿qué sería eso?

Haría la elección según los siguientes criterios

  • tipo de referencia vs semántica del tipo de valor. Si 2 objetos son solo iguales si son el mismo objeto, indica semántica de tipo de referencia => clase. Si el valor de sus miembros define la igualdad (por ejemplo, 2 DateTimes son iguales si ambos representan el mismo punto en el tiempo, incluso si son 2 objetos distintos), value type semántica => struct
  • Huella de memoria del objeto. Si el objeto es enorme y se asigna frecuentemente, convertirlo en una estructura consumiría la stack mucho más rápido, por lo tanto, preferiría tenerlo como clase. Por el contrario, preferiría evitar la penalización de GC para tipos de valor pequeño; por lo tanto, hazlos una estructura.
  • ¿puedes hacer que el objeto sea inmutable? Encuentro las estructuras ideales para ‘objetos de valor’, del libro DDD.
  • ¿Te enfrentas a un poco de boxeo-unboxing pena basada en el uso de este objeto? Si es así, ve a clase.

las estructuras deberían definirse inmutables donde en las clases no deberían. Si crees que tus objetos van a ser pequeños e inmutables , puedes continuar haciéndolos estructuras o dejarlos ser clases.

Una ventaja bastante buena, no tan conocida de Structs over Classes es que hay una implementación automática de GetHashcode e Igual en las estructuras.
Eso es bastante útil cuando se requieren claves para los diccionarios

La implementación de estructuras de GetHashcode e Equals se basa en el contenido binario de las instancias struct + reflection para los miembros de referencia (como los miembros de String y otras instancias de clases)

Entonces el siguiente código funciona para GethashCode / Equals:

 public struct Person { public DateTime Birthday { get; set; } public int Age{ get; set; } public String Firstname { get; set; } } class Program { static void Main(string[] args) { Person p1 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" }; Person p2 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" }; Debug.Assert(p1.Equals(p2)); Debug.Assert(p1.GetHashCode() == p2.GetHashCode()); } } 

Ambas aserciones tienen éxito cuando Persona es una estructura Ambas aserciones fallan si la Persona es una clase en lugar de una estructura

Referencia: https://msdn.microsoft.com/en-Us/library/2dts52z7%28v=vs.110%29.aspx

Saludos, mejor encoding

Realmente nunca puedo recordar, exactamente cómo las estructuras son diferentes, pero lo son. De maneras sutiles. De hecho, a veces vienen y te muerden.

Asi que. A menos que sepas lo que estás haciendo, solo sigue las clases.

Sé que esto suena un poco novato. Sé que debería ir ahora y buscar las diferencias y mostrarlas aquí, pero eso ya lo han hecho otros. Todo lo que digo es que agregar un tipo diferente de objetos crea una carga semántica, un poco de complejidad adicional que es prudente considerar cuidadosamente.

Si recuerdo correctamente, uno de los mayores problemas es la semántica del valor de las estructuras: Pasarlas dará lugar a diferentes objetos (a medida que pasan por valor). Si luego cambias un campo en un lugar, ¡ten en cuenta que en todos los demás lugares el campo no se modificó! ¡Es por eso que todos recomiendan la inmutabilidad para las estructuras!

EDITAR: para el caso que está describiendo, ¡las estructuras no funcionarán !

Un objeto de clase tiene la ventaja de que es posible pasar una referencia a él, con el scope y la duración de dicha referencia siendo ilimitada si alcanza el código externo. Una estructura tiene la ventaja de que, si bien es posible pasar referencias breves a ellas, no es posible pasar referencias promiscuas perpetuas. Esto ayuda a evitar tener que preocuparse si existen tales referencias.

Algunas personas han sugerido que los titulares de datos que son mutables no deberían ser estructuras. Estoy enfáticamente en desacuerdo. Las entidades que existen con el fin de almacenar datos deberían, en muchos casos, ser estructuras, especialmente si son mutables. Eric Lippert ha publicado muchas veces que considera que los tipos de valores mutables son malos (busque bajo las tags “mutable” y “struct”). Ciertamente es cierto que .net permite hacer ciertas cosas con estructuras mutables que no debería, y no permite algunas cosas convenientemente, sino POD (“Old Data”) que no tienen métodos de mutación, pero, en cambio, exponen todo su estado a través de campos públicos, tienen una consistencia muy útil en su comportamiento que no se comparte con ningún otro tipo de datos. El uso de una estructura POD puede confundir a alguien que no está familiarizado con su funcionamiento, pero hará que el progtwig sea mucho más legible para cualquiera que lo haga.

Considere, por ejemplo, el siguiente código, suponiendo que EmployeeInfoStruct no contiene más que tipos de valores y tipos de clases inmutables como String:

 [employeeInfoStruct es una estructura que contiene el siguiente campo]
 público Decimal AnualBonus;

 [algúnEmployeeContainer es una instancia de una clase que incluye el siguiente método]
 EmployeeInfoStruct GetEmployeeInfo (String id);  // Solo la firma - el código es inmaterial

 [algún otro método usa el siguiente código]
 EmployeeInfoStruct anEmployee = someEmployeeContainer.GetEmployeeInfo ("123-45-6789");
 anEmployee.YearlyBonus + = 100;

Eric Lippert se queja de que el código anterior alterará el valor en un empleado, pero ese cambio no tendrá ningún efecto en el contenedor. Yo sugeriría que eso es algo bueno: cualquiera que sepa cómo funciona el trabajo podría ver el código anterior y conocer las escrituras en una variable de estructura afectará esa variable, pero no afectará a ninguna otra cosa a menos que el progtwig use algún otro método (tal vez SetEmployeeInfo) para almacenar esa variable en algún lugar.

Ahora reemplace EmployeeInfoStruct con EmployeeInfoClass, que tiene una propiedad de lectura / escritura de tipo YearlyBonus. Usando solo la información anterior, ¿qué se puede decir acerca de la relación entre las escrituras en algún EmpleadorContenido y un Empleado? Dependiendo de las implementaciones de la clase de un empleado (que, a menos que EmployeeInfoClass esté sellado, podría o no ser realmente EmployeeInfoClass) y algúnEmployeeContainer, la relación entre los objetos podría ser cualquier cosa. Escribe a uno podría:

  1. No tiene efecto en el otro
  2. Actualice el otro de forma ‘natural’
  3. Corromper al otro de una manera arbitraria

Con estructuras que no contienen más que campos de cualquier tipo de valor o clases inmutables , la semántica siempre será # 1. Uno no tiene que mirar el código de la estructura en sí, ni el código del contenedor, para saberlo. Por el contrario, si el anEmployee.Salary o algúnEmployeeContainer.GetEmployee es virtual, es imposible saber realmente cuál será la semántica.

Es importante tener en cuenta que, si las estructuras son grandes, pasarlas por valor o devolverlas de las funciones puede ser costoso. En general, es mejor pasar estructuras grandes como parámetros ref cuando sea posible. Aunque las colecciones integradas realmente no hacen un buen trabajo al facilitar dicho uso, puede hacer que usar una estructura de cientos de bytes sea más económico que usar una clase.

El comentario sobre que las estructuras son inmutables es correcto. Y aquí es donde te puede morder. Puede definir estructuras con definidores de campo, pero cuando cambia un valor de campo, se crea una nueva instancia. Por lo tanto, si mantiene una referencia al objeto antiguo, seguirá haciendo referencia al valor anterior. No me gusta usar estucos mutables por esta razón, ya que esto puede producir errores sutiles y complejos (especialmente si usa declaraciones compuestas complejas).

Por otro lado, hay muchas buenas razones para usar clases con estado inmutable también (cadena de pensamiento).

Recuerdo un consejo dado en MSDN que struct no debería ser más grande que 16 o 21 bytes. Buscando el enlace, pero aún no lo puede encontrar.

La implicación principal fue que una vez que tiene una cadena en su tipo de datos, conviértalo en una clase sin pensar. De lo contrario, la estructura no debería contener mucho.

Creo que tienes la idea correcta. Las estructuras están hechas para imitar tipos de datos. Están basados ​​en el valor y no en la referencia. Si observa la documentación de MSDN para la mayoría de las clases de datos base (int, double, decimal, ect), todas están basadas en estructuras. Dicho esto, sin embargo, las estructuras no deben ser utilizadas en exceso por esa misma razón. El espacio para almacenar todo en esa estructura se asigna tan pronto como se instancia, mientras que las clases solo asignan espacio para una referencia a todo lo que hay dentro. Si los datos están en trozos lo suficientemente pequeños donde esto no es un problema, las estructuras son el camino a seguir. Si esto es un problema, vaya con las clases. Si no sabe, puede ser mejor que se quede con lo que está familiarizado.

Si tiene bajos requisitos de latencia y MUCHOS objetos, la recolección lenta de basura puede ser un problema. En ese caso, struct puede ser muy útil porque el recolector de elementos no utilizados no necesita escanear a través de una jerarquía de tipos de valores si los tipos de valores no contienen ningún tipo de referencia.

Puede encontrar un punto de referencia aquí: http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/