2010-02-19 20 views
16

Me pregunto por qué necesitamos struct si class puede hacer todo struct can y más? poner tipos de valor en clase no tiene ningún efecto secundario, creo.C# ¿por qué necesita struct si la clase puede cubrirlo?

EDIT: no puede ver ningún fuertes razones para utilizar struct

A struct es similar a una clase, con las siguientes diferencias clave:

  • A struct es un tipo de valor, mientras que una clase es un tipo de referencia.
  • Una estructura no admite la herencia (que no sea derivada implícitamente del objeto ).
  • Una estructura puede tener todos los miembros de una clase puede, excepto los siguientes:
  • Un constructor sin parámetros
  • un finalizador
  • miembros virtuales

Una estructura se utiliza en lugar de una clase cuando la semántica del tipo de valor es deseable. Buenos ejemplos de estructuras son tipos numéricos, donde es más natural que la asignación copie un valor en lugar de una referencia. Como una estructura es un tipo de valor, cada instancia no requiere creación de instancias de un objeto en el montón. Esto puede ser importante al crear muchas instancias de un tipo.

+0

Su pregunta está bastante bien cubierta aquí: http://stackoverflow.com/questions/13049/whats-the-difference-between-struct-and-class-in-net –

+7

¿Por qué hay una respuesta editada en el ¿pregunta? –

+1

posible duplicado de [¿Por qué necesitamos struct? (C#)] (http://stackoverflow.com/questions/1216993/why-do-we-need-struct-c) – nawfal

Respuesta

16

Los tipos de valores personalizados no son absolutamente necesarios - Java hace sin ellos, por ejemplo. Sin embargo, aún pueden ser útiles.

Por ejemplo, en Noda Time los estamos usando bastante extensamente, como una manera eficiente de representar cosas como instantes sin la sobrecarga de un objeto involucrado.

No diría que "la clase puede hacer toda la estructura puede y más" - se comportan de manera diferente, y deben pensarse de manera diferente.

+1

Lectura de la diferencia entre clase y estructura en los últimos días. Estoy bastante seguro de haber concentrado mi mente en la mayoría (¿todos?) De los matices (rendimiento en la situación "x", uso de la memoria en la situación "y", bla, bla, bla). Esto se simplifica demasiado, ¿pero diría que la siguiente afirmación es bastante precisa? Además de los tipos value-ref-ref, las clases son estructuras con capacidades adicionales pero también sobrecarga. A menos que necesite las capacidades adicionales o planifique pasar como un parm (el valor contra el tipo de referencia entra en juego aquí), las estructuras usan menos memoria y "funcionan mejor" que las clases. –

+0

@AndrewSteitz: No, no lo pondría de esa manera. El "tipo de valor frente al tipo de referencia" * es * la diferencia principal ... y dependiendo de cómo se usen, puede terminar usando mucha menos memoria con tipos de referencia que con tipos de valores. (Imagine un valor de 32 bytes, donde tiene 100 del mismo valor ... con un tipo de referencia podría tener 100 referencias al mismo objeto, frente a 100 copias de 32 bytes para el tipo de valor). –

+0

¡Muy agradecido! Pensé que me había preocupado por las diferencias, pero su énfasis en el valor versus la referencia es LA diferencia principal me dice que debería estudiar un poco más. –

2

las estructuras son también a menudo se requiere por razones de rendimiento. Las matrices de estructuras consumen bastante menos memoria y obtienen una coherencia del caché mucho mejor que una matriz de referencias a objetos. Esto es muy importante si trabajas con algo así como un sistema de renderizado y necesitas generar 5 millones de vértices, por ejemplo. Para obtener más información, consulte Rico Mariani's Performance Quiz + Answers.

0

Para el desarrollador de aplicaciones promedio, el uso de clases es la norma. En la superficie, las clases hacen que las estructuras parezcan innecesarias, pero cuando profundizas en los detalles, en realidad son bastante diferentes.

Consulte here para ver algunas diferencias entre las estructuras y las clases.

La diferencia más conocida es que las clases son tipos de referencia y las estructuras son tipos de valores. Esto es importante porque permite a un desarrollador de la biblioteca controlar cómo se pueden usar las instancias de un tipo de datos.

0

1, rendimiento. En algunos casos, al usar estructuras obtendremos un rendimiento mucho mejor.
2, inmutabilidad de datos. Al cambiar algunas propiedades de una estructura obtendrá una nueva estructura. Esto es muy útil en algunos casos
3, un mejor control de la representación en la memoria. Podemos definir exactamente cómo se ubica la estructura en la memoria y esto nos permite serializar y deserializar algunos datos binarios de manera rápida y eficiente.

2

Las estructuras son útiles simplemente porque se pasan por valor, lo que en realidad puede ser útil en ciertos algoritmos. Esto es realmente algo que las clases NO PUEDEN hacer.

struct ArrayPointer<T> 
{ 
    public T[] Array; 
    public int Offset; 
} 

Puede pasar esta estructura a un método y el método puede alterar el valor de compensación para sus propias necesidades. Mientras tanto, una vez que regrese del método, se comportará como si el desplazamiento nunca cambiara.

+0

No estaría a favor de ese ejemplo, ya que los cambios en la matriz en sí serán visibles para la persona que llama, a menos que el método llamado cree una nueva matriz. Un mejor ejemplo sería 'struct Point3d {public double X, Y, Z;}', donde uno desea saber que 'Point3d [100];' siempre contendrá 100 tripletas XYZ diferentes, en lugar de p. Ej. 100 referencias a una instancia común. – supercat

2

En general,, utilice una clase.

Usa solo una estructura cuando necesites semántica del tipo de valor.

+6

Los tipos de estructura no "solo viven en la pila". Una matriz de enteros no está en la pila. Un campo de clase de tipo int no está en la pila. Un campo estático de tipo int no está en la pila. Una variable local de tipo int que está en un bloque iterador no está en la pila. Una variable local de tipo int que es una variable externa cerrada de un método anónimo o una expresión lambda no está en la pila ... los tipos struct normalmente son * no * en la pila, por lo que dicen "solo pueden vivir en la pila" "es completamente engañoso. El valor de las estructuras es que puedes poner * muchas de ellas en el montón por poco dinero *. –

+1

No dije que las estructuras solo viven en la pila, dije que solo deberías * crear * una cuando quieras que solo viva en la pila. Todo lo que dijiste es absolutamente cierto, pero si la estructura solo vivirá mientras está encapsulada en algún otro tipo de datos, debería ser una clase. Solo crea una estructura cuando * debe * vivir en la pila. – Randolpho

+1

Pero no hay ningún tipo, struct o de otra manera, que * debe * vivir en la pila. Si lo que necesita es algo que * debe vivir en la pila *, entonces el idioma que desea utilizar es C, no C#. La razón para crear tipos de estructura es porque desea * modelar algo que es lógicamente un valor, no una referencia *. Está combinando "vidas en la pila", que es un detalle de implementación, con "tiene semántica de valor". Los tipos de valor tienen una semántica de valor sin importar dónde vivan. –

0

Es casi imprescindible usarlo para la interoperabilidad con la estructura de datos subyacente utilizada por la API win32. Supongo que una razón muy importante para tenerlo en .net podría deberse a esa razón.

9

¿Por qué usar un struct cuando funciona un class? Porque a veces un class no funciona.

Además de las razones de rendimiento mencionados por Reed Copsey (versión corta: un menor número de objetos que el GC necesita para realizar un seguimiento, permitiendo que el GC para hacer un mejor trabajo), hay un lugar donde deben utilizarse estructuras : P/Invoque a funciones que requieren estructuras de valor por defecto o miembros de estructura.

Por ejemplo, supongamos que desea invocar la función CreateProcess(). Supongamos además que desea utilizar una estructura STARTUPINFOEX para el parámetro lpStartupInfo en CreateProcess().

Bueno, ¿qué es STARTUPINFOEX? Este:

typedef struct _STARTUPINFOEX { 
    STARTUPINFO     StartupInfo; 
    PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; 
} STARTUPINFOEX, *LPSTARTUPINFOEX; 

en cuenta que STARTUPINFOEX contiene STARTUPINFO como su primer miembro. STARTUPINFO es una estructura.

Dado que las clases son los tipos de referencia, si declaramos la correspondiente C# tipo así:

[StructLayout(LayoutKind.Sequential)] 
class STARTUPINFO { /* ... */ } 

class STARTUPINFOEX { public STARTUPINFO StartupInfo; /* ... */ } 

La disposición en memoria correspondiente sería mal, como STARTUPINFOEX.StartupInfo sería un puntero(4 bytes en Plataformas ILP32), NO una estructura (como se requiere, 68 bytes de tamaño en plataformas ILP32).

Así, para apoyar la invocación de funciones arbitrarias que aceptan estructuras arbitrarias (que es lo que P/Invoke se trata), una de dos cosas son necesarias:

  1. tipos de valores totalmente de apoyo.Esto permite que C# declare un tipo de valor para STARTUPINFO que tendrá el diseño correcto en memoria para calcular referencias (es decir, compatibilidad con struct, como C# tiene).

  2. Alguna sintaxis alternativa dentro de P/estructuras invocables que informaría al contador de referencias en tiempo de ejecución que este miembro debería distribuirse como un tipo de valor en lugar de como un puntero.

(2) es una solución viable (y puede haber sido utilizado en J/Direct en Visual J ++; no me acuerdo), pero dado que los tipos de valores propios son más flexibles, permiten una serie de prestaciones optimizaciones que de otro modo no podrían lograrse, y hacen un uso sensato dentro de los escenarios de P/Invoke, no sorprende que C# sea compatible con los tipos de valores.

Cuestiones relacionadas