2009-08-28 23 views
42

Hay muchas preguntas aquí en SO que tratan con las diferencias entre Structs y Classes en C#, y cuándo usar una u otra. (La respuesta de una oración: use structs si necesita semántica de valores.) Existen muchas pautas sobre cómo elegir una u otra, la mayoría de las cuales se reducen a: usar una clase a menos que cumpla con estos requisitos específicos, luego use una estructuraEstructuras: ejemplos de la vida real?

Todo esto tiene sentido para mí.

Sin embargo, parece que no puedo encontrar ningún ejemplo real de personas que utilizan estructuras en un sistema. Soy (semi) nuevo en C#, y tengo problemas para imaginar una situación concreta donde las estructuras son realmente la elección correcta (al menos, no me he encontrado con ninguna).

Así que, me convierto al SO mundo-cerebro. ¿Cuáles son algunos casos en los que realmente usaste una estructura en un sistema donde una clase no habría funcionado?

+12

Cambiaría tu respuesta de una frase para "solo usar clases a menos que realmente sepas lo que estás haciendo" –

+1

@Joel - ¡Esa es una regla con la que puedo vivir! –

+5

@Joel: el único problema con esa idea es que la mayoría de las personas solo piensan que saben lo que hacen. – chsh

Respuesta

5

El ejemplo por excelencia son los marcos nullable types, como int?. Estos usan estructuras para que conserven la semántica del valor de un int, pero proporcionan una forma de hacer que sean nulos sin el boxeo y convertirlos en tipos de referencia.

Utilizaría una estructura cuando no desea pasar algo por referencia. Supongamos que tiene una colección de datos, o un objeto que desea pasar por valor (es decir, todo lo que le pasa es trabajar con su propia copia única, no una referencia a la versión original), entonces una estructura es el tipo correcto para utilizar.

+0

No estoy seguro de que los tipos anulables sean un gran ejemplo.En general deberían evitarse en los casos en que la velocidad es importante, y si 'Nullable ' era un tipo de referencia que simplemente contenía un solo campo de tipo 'T', en algunos casos puede ser más lento que una estructura' Nullable 'pero no lo haría No tiene la semántica extraña que surge del hecho de que un 'Nullable ' de valor vacío no es una referencia nula, pero se parece a uno. – supercat

20

Bueno, una clase todavía funcionaría para eso, pero un ejemplo que podría pensar es algo así como un punto. Suponiendo que sea un valor xey, podrías usar una estructura.

struct Point { 
    int x; 
    int y; 
} 

En mi mente, yo preferiría tener una representación más simple de un par de enteros que de definir el uso de una clase con instancias cuando la entidad real en realidad no tiene mucho (o cualquier) comportamiento.

+0

Oh, sí, +1 por hacerlo simple ... – RSolberg

+1

entonces ¿tu x y y son variables privadas? :) –

+0

ah sí, mi programación en C a veces se filtra ... –

17

I utilizó un struct para representar un Geolocalización

struct LatLng 
{ 
    public decimal Lattitude 
    { 
     get; 
     set; 
    } 
    public decimal Longitude 
    { 
     get; 
     set; 
    } 
} 

esto representa una única entidad, por ejemplo puedo añadir 2 LatLng de juntas o realizar otras operaciones en esta entidad única.

MSDN-struct

El tipo de estructura es adecuada para representar objetos ligeros tales como punto, un rectángulo, y el color. Aunque es posible representar un punto como una clase, una estructura es más eficiente en algunos escenarios. Para el ejemplo , si declara una matriz de objetos de 1000 puntos, asignará memoria adicional para hacer referencia a cada objeto . En este caso, la estructura es menos costosa.

Además, si nos fijamos en los tipos primitivos Int32, decimales, doble .etc se dará cuenta de que son todas las estructuras , lo que les permite ser los tipos de valor, mientras que lo que les permite poner en práctica ciertas interfaces cruciales.

+2

Downvoated, la mayoría de estos ejemplos no explica POR QUÉ eliges una estructura sobre una clase. –

+2

Simplemente agregando entidades individuales no es una razón válida para elegir una estructura sobre una clase. Puedes usar ambos para eso, así que no veo cómo eso responde la pregunta. Y sí, él pidió ejemplos, pero un ejemplo debería explicar por qué estás usando una estructura y no una clase. –

+0

+1 ¡Como utilicé una estructura para exactamente lo mismo! –

7

Una estructura de dinero es probablemente una de las más comunes, sin embargo, el número de teléfono o la dirección también son comunes.

public struct Money 
{ 
    public string Currency { get; set; } 
    public double Amount { get; set; } 
} 

public struct PhoneNumber 
{ 
    public int Extension { get; set; } 
    public int RegionCode { get; set; } 
    //... etc. 
} 

public struct FullName 
{ 
    public string FirstName { get; set; } 
    public string MiddleName { get; set; } 
    public string LastName { get; set; } 
} 

Tenga en cuenta sin embargo que en .NET sus estructuras no deben ser más grandes en el consumo de memoria de 16 bytes, ya que si se hacen más grandes que el CLR tiene que asignar memoria adicional.

También porque las estructuras 'en vivo' en la pila (y no en el montón como tipos de referencia) pueden considerar el uso de estructuras si necesita crear instancias de muchos de los mismos tipos de objetos.

+4

Guau, realmente te equivocaste. Son 16 BYTES, no 16 kilobytes ...;) – Guffa

+0

Wow, tienes razón :) ¡gracias por eso! – Stephanvs

+0

Edité la respuesta para reflejar su corrección Guffa. – swilliams

13

Las estructuras también se usan generalmente en sistemas de gráficos/renderizado. Hay muchos beneficios en hacer estructuras de puntos/vectores.

Rico Mariani publicó una excelente quiz on value based programming. Discutió muchas razones para preferir las estructuras en situaciones específicas, y las explicó en detalle en su quiz results post.

+0

Love ese enlace.V Útil – zebrabox

+0

Me gusta ese enlace, aunque sugeriría que todas las infracciones de "reglas" podrían corregirse agregando una regla, básicamente diciendo que si uno quiere algo que simplemente se comportará como un grupo de variables relacionadas pero independientes unidas con cinta adhesiva, no necesitará ningún otro tipo de comportamiento, y no quiere ningún concepto de identidad, generalmente se debe usar una estructura, a pesar de otras "reglas". – supercat

1

La clave para mí es definir si deseo mantener la referencia al mismo objeto.

que hace sentido cuando struct es parte de otra entidad, pero lo hace la entidad misma.

En el ejemplo anterior con LatLong que hace perfecto sentido, por ejemplo. Necesita copiar valores de un objeto a otro, no seguir consultando el mismo objeto.

0

Básicamente, utilizo Structs para modelar datos geométricos y matemáticos, o cuando quiero una estructura de datos basada en el valor.

3

Proporcionan una implementación predeterminada para Object.GetHashCode(), por lo que es posible que desee utilizar una estructura en lugar de una clase cuando el objeto es una colección simple de tipos de no referencia que desea utilizar como claves de un diccionario .

También son útiles para PInvoke/interoperabilidad o escenarios de red de bajo nivel donde se desea un control preciso sobre el diseño binario de una estructura de datos. (Vaya a www.pinvoke.net para obtener muchos códigos de interoperabilidad que requieren estructuras)

Pero en realidad, nunca los uso yo. No te preocupes por no usarlos.

+0

De hecho, descubrí la diferencia GetHashCode() en algún código en el que he estado trabajando, ¡muy genial! Casi siento que es digno de una pregunta por sí solo (para fines de referencia). –

1

A menudo uso structs para representar un tipo de valor de modelo de dominio que podría representarse como una enumeración, pero necesita un número arbitrario ilimitado de valores discretos, o quiero que tenga un comportamiento adicional (métodos) que no puede agregar a un enum ... Por ejemplo, en un proyecto reciente, muchos elementos de datos se asociaron con un mes calendario específico en lugar de con una fecha.Así que creé una estructura CalendarMonth que tenía métodos:

  • CalendarMonth Parse estático (DateTime inValue);
  • CalendarMonth Parse estático (string inValue);
método TryParse()

y,

  • estática bool TryParse (inValue cuerda, fuera CalendarMonth outVal);

Y Propiedades

  • int Mes {get; conjunto; }
  • int Año {get; conjunto; }
  • DateTime StartMonthLocal {get; conjunto; }
  • DateTime StartMonthUTC {get; conjunto; }
  • DateTime EndMonthLocal {get; conjunto; }
  • DateTime EndMonthUTC {get; conjunto; }

etc.

2

Básicamente trato de no utilizarlas. Encuentro que confunden a otros desarrolladores en el equipo y, por lo tanto, no vale la pena el esfuerzo. Solo encontré un caso para usarlo, un tipo de Enum personalizado usamos un generador de código para producir desde XML.

0

La única vez que he usado una estructura cuando se estaba construyendo una estructura de fracción:

public struct Fraction 
{ 
    public int Numerator {get;set;} 
    public int Denominator {get; set;} 
    //it then had a bunch of Fraction methods like Reduce, Add, Subtract etc... 
} 

sentí que representa un valor, al igual que el construido en los tipos de valor, y por lo tanto la codificación contra se sentiría más natural si se comportara como un tipo de valor.

1

En general, no me preocupo por la "densidad de datos" en mis aplicaciones comerciales. normalmente siempre voy a utilizar una clase a menos que específicamente quiero semántica de valor

esto significa que estoy previendo una situación en la que quiero comparar dos de estas cosas y no quiero que se mostrará como el mismo si tienen la mismo valor. Con las clases esto es realmente más trabajo porque necesito anular ==,! =, Igual, y GetHashcode, que incluso si resharper lo hace por mí, es un código extra innecesario.

Así que en mi mente, siempre se utilizan las clases a menos que sepa que desea estas cosas que han de compararse por valor (en este caso, valor de componente)

0

creo que el .Net Framework es la vida del todo real. Ver la lista bajo "Estructuras":

System Namespace

0

En algunas situaciones de rendimiento crítico, una estructura (un tipo de valor y por lo tanto asignado de la pila) puede ser mejor que una clase (un tipo de referencia y por lo tanto asignado del montón). La publicación de blog de Joe Duffy "A single-word reader/writer spin lock" muestra una aplicación real de esto.

0

Uno que he creado en el pasado es StorageCapacity.Representaba de 0 bytes a N exabytes (podría haber ido más arriba al yottabyte, pero exa parecía suficiente en ese momento). La estructura tiene sentido ya que trabajé para una empresa de gestión de almacenamiento. Podrías pensar que era bastante simple: una estructura con una StorageUnit (enumeración) y una Quantity (utilicé decimal). Pero cuando agrega conversiones, operadores y clases para admitir el formateo, el análisis sintáctico, etc., se suma.

La abstracción fue útil para permitirle tomar cualquier StorageCapacity y representarlo como bytes, kilobytes, etc. sin tener que multiplicar o dividir por 1024 muchas veces.

0

me han dado mis razones para el uso de estructuras ya en otro lugar (When to use struct in C#), y me han utilizado estructuras de estos motivos en los proyectos de la vida real:

que elegiría para utilizar estructuras por razones de rendimiento si necesitaba para almacenar un gran número del mismo tipo de elemento en una matriz, lo que puede ocurrir en el procesamiento de imágenes.

Uno necesita usar estructuras para pasar datos estructurados entre C# y C++.

A menos que tenga una muy buena razón para usarlas, trato de evitarlas.

Sé que a algunas personas les gusta usarlas para implementar semántica de valores pero encuentro que este comportamiento es muy diferente del comportamiento de asignación "normal" de las clases (en C#) que uno se encuentra con errores difíciles de rastrear porque uno no recordaba que el objeto al que se estaba asignando desde o tenía este comportamiento porque se implementó como una estructura en lugar de una clase. (Me ha pasado más de una vez, así que doy esta advertencia ya que en realidad me he quemado por el uso imprudente de las estructuras de C#)

+0

Algunas personas se quejan de que la semántica de valores es confusa, pero me llama la atención cómo estas personas evitan confundirse por el hecho de que una referencia de clase puede usarse para encapsular identidad, estado mutable, ambos o ninguno y el efecto de copiar una referencia dependerá de lo que esté encapsulado por eso. Dado un 'SomeClass [100]', hay al menos tres enfoques diferentes que pueden ser necesarios para copiar el estado encapsulado por el elemento 3 en el elemento 4. ¿Cómo se debe saber cuál usar en cada situación, sin examinar todos los demás? código que usa la matriz? – supercat

1

Así que entiendo que nunca has usado DateTime (una estructura).

0

No estoy seguro de cuánto usará esto, pero hoy descubrí que si bien no puede tener intializadores de campo de instancia en estructuras, puede hacerlo en clases.

Por lo tanto, el siguiente código dará errores de compilación, pero si cambia la "estructura" a "clase" compila.

public struct ServiceType 
    { 
     public bool backEnd { get; set; } 
     public bool frontEnd { get; set; } 

     public string[] backEndServices = { "Service1", "Service2" }; 
     public string[] frontEndServices = { "Service3", "Service4" }; 
    } 
1

No puedo creer que nadie ha mencionado XNA: en XNA, casi todo es un struct. Entonces cuando lo haga

Matrix rotation = Matrix.CreateRotationZ(Math.PiOver2); 

Usted realmente está creando un tipo de valor.

Esto se debe a que, a diferencia de la programación de aplicaciones, un puesto de unos pocos milisegundos, mientras que el colector de basura se ejecuta no es aceptable (sólo obtener 16,6 ms para procesar toda la imagen!), así que tenemos que evitar asignaciones como tanto como sea posible para que el GC no tenga que correr tanto.

Esto es especialmente true en el XBox 360, donde el GC no está cerca de la calidad que tiene en la PC, ¡incluso un promedio de una asignación por cuadro puede matar el rendimiento!

0

una estructura en C# es en su corazón ni más ni menos que un montón de variables pegados con cinta adhesiva. Si uno quiere que cada variable de un tipo en particular represente un grupo de variables independientes pero relacionadas (como las coordenadas de un punto) pegadas con cinta adhesiva, a menudo es mejor usar una estructura de campo expuesto que una clase, independientemente de si "montón" significa dos o veinte. Tenga en cuenta que aunque el consejo de struct-versus-class de Microsoft es bueno para los tipos de datos que encapsulan un único valor, se debe considerar inaplicable para los tipos cuyo objetivo es encapsular valores independientes pero relacionados. Cuanto mayor sea el grado en que las variables son independientes, mayores serán las ventajas de usar una estructura de campo expuesto.

Si se desea utilizar una clase para encapsular un grupo de variables independientes, hay dos maneras en que puede hacerlo, ninguna de las cuales es terriblemente conveniente. Se puede usar una clase inmutable, en cuyo caso cualquier ubicación de almacenamiento no nulo de ese tipo de clase encapsulará los valores mantenidos por la instancia identificada de ese modo, y una ubicación de almacenamiento se puede copiar a otra para hacer que la nueva encapsule esos mismos valores. Desafortunadamente, cambiar uno de los valores encapsulados por una ubicación de almacenamiento generalmente requerirá construir una nueva instancia que sea exactamente igual a la antigua excepto que se modifique ese valor. Por ejemplo, si uno tiene una variable pt del tipo Immutable3dPoint y una desea aumentar pt.X en uno, uno tendría que hacer algo como: pt = new Immutable3dPoint(pt.X+1, pt.Y, pt.Z); Quizás tolerable si el tipo solo encapsula tres valores, pero bastante molesto si hay muchos.

El otro enfoque basado en clases es usar una clase mutable; esto generalmente requiere que uno se asegure de que cada ubicación de almacenamiento del tipo de clase contenga la única referencia en cualquier parte del universo a una instancia de esa clase. Cuando se crea una ubicación de almacenamiento, se debe construir una nueva instancia y almacenar allí una referencia. Si desea copiar los valores de la ubicación de almacenamiento P a la ubicación de almacenamiento Q, a otro, debe copiar todos los campos o propiedades de una instancia a la otra (quizás haciendo que el tipo implemente un método CopyFrom y diciendo Q.CopyFrom(P);. si en cambio dice Q=P; que pueden parecer a trabajar, pero los futuros intentos de modificar P también modificarán Q y viceversa. clases mutables pueden trabajar, y que a veces puede ser eficaz, pero es muy fácil de estropear las cosas.

Las estructuras de campo expuesto combinan la conveniente semántica de copia de valores de las clases inmutables con las convenientes modificaciones por partes permitidas por las clases mutables. Las estructuras grandes son más lentas de copiar que las referencias a obj inmutables. Sin embargo, el costo de modificar parte de una estructura de campo expuesto depende únicamente del alcance de la modificación, más que del tamaño total de la estructura. Por el contrario, el costo de cambiar una pieza de datos encapsulada en un tipo de clase inmutable será proporcional al tamaño total de la clase.

Cuestiones relacionadas