En .NET, hay dos categorías de tipos, tipos de referencia y tipos de valores .
Las estructuras son tipos de valor y las clases son tipos de referencia .
La diferencia general es que un tipo de referencia vive en el montón y un tipo de valor vive en línea, es decir, donde sea que esté definida su variable o campo.
Una variable que contiene un tipo de valor contiene todo el valor del tipo de valor. Para una estructura, eso significa que la variable contiene toda la estructura, con todos sus campos.
Una variable que contiene un tipo de referencia contiene un puntero o una referencia a otro lugar de la memoria donde reside el valor real.
Esto tiene un beneficio, para empezar:
- los tipos de valor siempre contienen un valor
- los tipos de referencia pueden contener una referencia nula , lo que significa que no se refieren a nada en este momento
Internamente, los tipos de referencia se implementan como punteros, y sabiendo eso, y sabiendo cómo funciona la asignación de variables, existen otros patrones de comportamiento:
- copiando el contenido de una variable de tipo de valor en otra variable, copia todo el contenido en la nueva variable, haciendo que los dos sean distintos. En otras palabras, después de la copia, los cambios en uno no afectarán al otro.
- copiando el contenido de una variable de tipo de referencia en otra variable, copia la referencia, lo que significa que ahora tiene dos referencias a la misma en otro lugar para almacenar los datos reales. En otras palabras, después de la copia, el cambio de los datos en una referencia también afectará a la otra, pero solo porque en realidad solo está mirando los mismos datos en ambos lugares
Cuando declara variables o campos, así es como difieren los dos tipos:
- variable: el tipo de valor vive en la pila, el tipo de referencia vive en la pila como un puntero a algún lugar en la memoria del montón donde vive la memoria real (aunque tenga en cuenta la serie de artículos Eric Lipperts: La pila es un detalle de implementación ).
- class / struct-field: el tipo de valor vive completamente dentro del tipo, el tipo de referencia vive dentro del tipo como un puntero a algún lugar en la memoria del montón donde vive la memoria real.
Un breve resumen de cada uno:
Solo clases:
Solo estructuras:
Ambas clases y estructuras:
fuente
c# struct memory overhead
y encontré esta respuesta de Hans Passant que dice que no, ese tampoco es el caso. Entonces, ¿ qué quiere decir?class
memoria gestionada (manejada por el recolector de basura), mientras que las instancias destruct
no .En .NET, las declaraciones de estructura y clase diferencian entre tipos de referencia y tipos de valor.
Cuando pasa un tipo de referencia, solo hay uno realmente almacenado. Todo el código que accede a la instancia está accediendo al mismo.
Cuando pasa un tipo de valor, cada uno es una copia. Todo el código está trabajando en su propia copia.
Esto se puede mostrar con un ejemplo:
Para una clase esto sería diferente
Las clases pueden ser nada: la referencia puede apuntar a un valor nulo.
Las estructuras son el valor real: pueden estar vacías pero nunca nulas. Por esta razón, las estructuras siempre tienen un constructor predeterminado sin parámetros: necesitan un 'valor inicial'.
fuente
Diferencia entre estructuras y clases:
fuente
De la elección de Microsoft entre clase y estructura ...
fuente
Además de todas las diferencias descritas en las otras respuestas:
Si está buscando un video que explique todas las diferencias, puede consultar la Parte 29 - Tutorial de C # - Diferencia entre clases y estructuras en C # .
fuente
Las instancias de clases se almacenan en el montón administrado. Todas las variables que 'contienen' una instancia son simplemente una referencia a la instancia en el montón. Al pasar un objeto a un método, se pasa una copia de la referencia, no el objeto en sí.
Las estructuras (técnicamente, los tipos de valor) se almacenan donde sea que se usen, de forma muy similar a un tipo primitivo. El contenido puede ser copiado por el tiempo de ejecución en cualquier momento y sin invocar un constructor de copias personalizado. Pasar un tipo de valor a un método implica copiar todo el valor, de nuevo sin invocar ningún código personalizable.
Los nombres de C ++ / CLI mejoran la distinción: "clase de referencia" es una clase como se describe primero, "clase de valor" es una clase como se describe en segundo lugar. Las palabras clave "clase" y "estructura" tal como las usa C # son simplemente algo que debe aprenderse.
fuente
fuente
Estructura vs clase
Una estructura es un tipo de valor, por lo que se almacena en la pila, pero una clase es un tipo de referencia y se almacena en el montón.
Una estructura no admite la herencia y el polimorfismo, pero una clase admite ambos.
Por defecto, todos los miembros de la estructura son públicos, pero los miembros de la clase son de naturaleza privada por defecto.
Como una estructura es un tipo de valor, no podemos asignar nulo a un objeto de estructura, pero no es el caso de una clase.
fuente
Para agregar a las otras respuestas, hay una diferencia fundamental que vale la pena señalar, y es cómo se almacenan los datos dentro de las matrices, ya que esto puede tener un efecto importante en el rendimiento.
Entonces, una matriz de estructuras se ve así en la memoria
[struct][struct][struct][struct][struct][struct][struct][struct]
Mientras que una variedad de clases se ve así
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
Con una matriz de clases, los valores que le interesan no se almacenan dentro de la matriz, sino en otra parte de la memoria.
Para la gran mayoría de las aplicaciones, esta diferencia realmente no importa, sin embargo, en el código de alto rendimiento esto afectará la localidad de los datos dentro de la memoria y tendrá un gran impacto en el rendimiento del caché de la CPU. El uso de clases cuando podría / debería haber utilizado estructuras aumentará enormemente el número de errores de caché en la CPU.
Lo más lento que hace una CPU moderna es no acumular números, está obteniendo datos de la memoria y un acierto de caché L1 es muchas veces más rápido que leer datos de la RAM.
Aquí hay un código que puede probar. En mi máquina, iterar a través de la matriz de clase tarda ~ 3 veces más que la matriz de estructura.
fuente
Solo para completarlo, hay otra diferencia al usar el
Equals
método, que es heredado por todas las clases y estructuras.Digamos que tenemos una clase y una estructura:
y en el método Main, tenemos 4 objetos.
Entonces:
Entonces , las estructuras son adecuadas para objetos numéricos, como puntos (guardar coordenadas x e y). Y las clases son adecuadas para otros. Incluso si 2 personas tienen el mismo nombre, altura, peso ..., siguen siendo 2 personas.
fuente
Bueno, para empezar, una estructura se pasa por valor en lugar de por referencia. Las estructuras son buenas para estructuras de datos relativamente simples, mientras que las clases tienen mucha más flexibilidad desde el punto de vista arquitectónico a través del polimorfismo y la herencia.
Otros probablemente pueden darle más detalles que yo, pero uso estructuras cuando la estructura que busco es simple.
fuente
Además de la diferencia básica del especificador de acceso, y los pocos mencionados anteriormente, me gustaría agregar algunas de las principales diferencias, incluidas algunas de las mencionadas anteriormente, con una muestra de código con salida, que dará una idea más clara de la referencia y el valor
Estructuras:
Clase:
Muestra de código
Salida
El valor inicial del objeto Struct es: 10
Método de estructura interna El valor del método interno del objeto de estructura es: 20
El valor de llamada después del método del objeto Struct es: 10
El valor inicial del objeto de clase es: 10
Método de clase interior El valor del método interior del objeto de clase es: 20
El valor de llamada después del método del objeto de clase es: 20
Aquí puede ver claramente la diferencia entre llamada por valor y llamada por referencia.
fuente
Los eventos declarados en una clase tienen su acceso + = y - = bloqueado automáticamente a través de un bloqueo (esto) para que sean seguros para subprocesos (los eventos estáticos están bloqueados en el tipo de la clase). Los eventos declarados en una estructura no tienen su acceso + = y - = bloqueado automáticamente. Un bloqueo (esto) para una estructura no funcionaría ya que solo puede bloquear una expresión de tipo de referencia.
Crear una instancia de estructura no puede causar una recolección de basura (a menos que el constructor cree directa o indirectamente una instancia de tipo de referencia) mientras que crear una instancia de tipo de referencia puede causar una recolección de basura.
Una estructura siempre tiene un constructor público predeterminado incorporado.
Esto significa que una estructura siempre es instanciable, mientras que una clase podría no serlo ya que todos sus constructores podrían ser privados.
Una estructura no puede tener un destructor. Un destructor es solo una anulación del objeto. Finalizar disfrazado, y las estructuras, que son tipos de valor, no están sujetas a la recolección de basura.
Una estructura está sellada implícitamente, una clase no.
Una estructura no puede ser abstracta, una clase sí.
Una estructura no puede llamar a: base () en su constructor, mientras que una clase sin una clase base explícita sí puede.
Una estructura no puede extender otra clase, una clase puede.
Una estructura no puede declarar miembros protegidos (por ejemplo, campos, tipos anidados) que una clase puede.
Una estructura no puede declarar miembros de funciones abstractas, una clase abstracta sí.
Una estructura no puede declarar miembros de funciones virtuales, una clase puede.
Una estructura no puede declarar miembros de función sellados, una clase puede.
Una estructura no puede declarar miembros de la función de anulación, una clase puede.
La única excepción a esta regla es que una estructura puede anular los métodos virtuales de System.Object, viz, Equals (), y GetHashCode (), y ToString ().
fuente
Object
, que contendría una referencia a una copia en caja de la estructura.Como se mencionó anteriormente: las clases son de tipo de referencia, mientras que las estructuras son tipos de valor con todas las consecuencias.
Como pulgar de la regla, las Pautas de diseño de marcos recomiendan usar estructuras en lugar de clases si:
fuente
Hay un caso interesante de rompecabezas "clase vs estructura": situación en la que necesita devolver varios resultados del método: elija cuál usar. Si conoce la historia de ValueTuple, sabe que ValueTuple (struct) se agregó porque debería ser más efectivo que Tuple (clase). Pero, ¿qué significa en números? Dos pruebas: una es struct / class que tiene 2 campos, otra con struct / class que tiene 8 campos (con una dimensión de más de 4 - la clase debería ser más efectiva que la estructura en términos de ticks del procesador, pero por supuesto también se debe considerar la carga de GC )
PD Otro punto de referencia para el caso específico 'sturct o clase con colecciones' está allí: https://stackoverflow.com/a/45276657/506147
Prueba de código:
fuente
Esto es cierto, sin embargo, también tenga en cuenta que a partir de .NET 2 las estructuras admiten una versión Nullable y C # proporciona algo de azúcar sintáctico para que sea más fácil de usar.
fuente
(object)(default(int?)) == null
que no se puede hacer con ningún otro tipo de valor, porque aquí hay más que solo azúcar. El único azúcar esint?
paraNullable<int>
.Cada variable o campo de un tipo de valor primitivo o tipo de estructura contiene una instancia única de ese tipo, incluidos todos sus campos (públicos y privados). Por el contrario, las variables o los campos de los tipos de referencia pueden ser nulos, o pueden referirse a un objeto, almacenado en otro lugar, al que también puede existir cualquier número de otras referencias. Los campos de una estructura se almacenarán en el mismo lugar que la variable o el campo de ese tipo de estructura, que puede estar en la pila o puede ser parte de otro objeto de montón.
Crear una variable o campo de un tipo de valor primitivo lo creará con un valor predeterminado; La creación de una variable o campo de un tipo de estructura creará una nueva instancia, creando todos los campos de la manera predeterminada. La creación de una nueva instancia de un tipo de referencia comenzará creando todos los campos allí de la manera predeterminada, y luego ejecutará un código adicional opcional según el tipo.
Copiar una variable o campo de un tipo primitivo a otro copiará el valor. Copiar una variable o campo de tipo de estructura a otra copiará todos los campos (públicos y privados) de la primera instancia a la última instancia. Copiar una variable o campo de tipo de referencia a otra hará que esta última haga referencia a la misma instancia que la primera (si la hay).
Es importante tener en cuenta que en algunos lenguajes como C ++, el comportamiento semántico de un tipo es independiente de cómo se almacena, pero eso no es cierto para .NET. Si un tipo implementa una semántica de valor mutable, al copiar una variable de ese tipo a otra, se copian las propiedades de la primera a otra instancia, a las que se refiere la segunda, y el uso de un miembro de la segunda para mutarlo hará que se cambie esa segunda instancia. , pero no el primero. Si un tipo implementa una semántica de referencia mutable, copiar una variable a otra y usar un miembro de la segunda para mutar el objeto afectará al objeto mencionado por la primera variable; los tipos con semántica inmutable no permiten la mutación, por lo que no importa semánticamente si la copia crea una nueva instancia o crea otra referencia a la primera.
En .NET, es posible que los tipos de valor implementen cualquiera de las semánticas anteriores, siempre que todos sus campos puedan hacer lo mismo. Sin embargo, un tipo de referencia solo puede implementar semántica de referencia mutable o semántica inmutable; Los tipos de valores con campos de tipos de referencia mutables se limitan a implementar semántica de referencia mutable o semántica híbrida extraña.
fuente