c# - example - Structs-ejemplos de la vida real?
struct c# ejemplos (21)
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 una enumeración ... 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:
- static CalendarMonth Parse (DateTime inValue);
- static CalendarMonth Parse (string inValue);
y el método TryParse (),
- static bool TryParse (string inValue, out CalendarMonth outVal);
Y propiedades
- int Month {get; conjunto; }
- int Año {get; conjunto; }
- DateTime StartMonthLocal {get; conjunto; }
- DateTime StartMonthUTC {get; conjunto; }
- DateTime EndMonthLocal {get; conjunto; }
- DateTime EndMonthUTC {get; conjunto; }
etc.
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 estructura
Todo esto tiene sentido para mí.
Sin embargo, parece que no puedo encontrar ejemplos reales de personas que usen 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, todavía no me he topado con una).
Entonces, me dirijo al SO-cerebro mundial. ¿Cuáles son algunos casos en los que realmente usaste una estructura en un sistema donde una clase no habría funcionado?
Así que supongo que nunca usaste DateTime (una estructura).
Básicamente trato de NO usarlos. 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.
Básicamente, utilizo Structs para modelar datos geométricos y matemáticos, o cuando quiero una estructura de datos basada en el valor.
Bueno, una clase todavía funcionaría, pero un ejemplo en el que podría pensar es algo así como un Point. Suponiendo que sea un valor xey, podrías usar una estructura.
struct Point {
int x;
int y;
}
En mi opinión, preferiría tener una representación más simple de un par de números enteros que definir un uso de una clase con instancias cuando la entidad real realmente no tiene mucha (o ninguna) conducta.
Creo que .Net Framework es bastante real. Vea la lista bajo "Estructuras":
El ejemplo por excelencia es el framework de 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ías una estructura cuando no quieres 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.
En algunas situaciones de rendimiento crítico, una estructura (un tipo de valor y, por tanto, asignada desde la pila) puede ser mejor que una clase (un tipo de referencia y, por lo tanto, asignada desde el montón). La publicación del blog de Joe Duffy " Un bloqueo de rotación de lector / escritor de una sola palabra " muestra una aplicación de la vida real de esto.
En general, no me preocupo por la "densidad de datos" en mis aplicaciones comerciales. Por lo general, siempre uso una clase a menos que desee específicamente una semántica de valores
esto significa que estoy previendo una situación en la que quiero comparar dos de estas cosas y quiero que aparezcan igual si tienen el mismo valor. Con las clases esto es en realidad más trabajo porque necesito anular ==,! =, Equals y GetHashcode, que incluso si resharper lo hace por mí, es un código extra innecesario.
Entonces, en mi mente, siempre use clases a menos que sepa que quiere que estas cosas se comparen por valor (en este caso, valor del componente)
He dado mis razones para usar estructuras ya en otro lugar ( Cuándo usar struct en C # ), y he utilizado estructuras por estos motivos en proyectos de la vida real:
Elegiría utilizar estructuras por motivos de rendimiento si tuviera que 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 usarlos, trato de evitarlos.
Sé que a algunas personas les gusta usarlas para implementar la semántica de valores, pero considero 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 recuerda que el objeto que se estaba asignando desde o hacia tenía este comportamiento porque se implementó como una estructura en lugar de una clase. (Me ha sucedido 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 #).
He estado trabajando en instituciones financieras donde se lograron requisitos de caché y latencia a gran escala mediante el uso de estructuras. Básicamente, las estructuras pueden ahorrarle al colector de basura MUCHO trabajo.
Vea estos ejemplos:
http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/ http://00sharp.wordpress.com/2013/07/04/a-case-for-the-structpart-2/
La única vez que utilicé una estructura fue cuando 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 los tipos de valores incorporados, y por lo tanto la codificación en contra de este se sentiría más natural si se comportase como un tipo de valor.
La clave para mí es definir si quiero mantener referencia al mismo objeto.
Lo cual hace que sence cuando struct forma 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.
Las estructuras también se usan típicamente en sistemas de gráficos / renderizado. Hay muchos beneficios en hacer estructuras de puntos / vectores.
Rico Mariani publicó una excelente prueba sobre programación basada en valores . Discutió muchas razones para preferir las estructuras en situaciones específicas, y las explicó en detalle en la publicación de resultados de su prueba .
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" };
}
No puedo creer que nadie haya mencionado XNA : en XNA, casi todo es una struct
. Entonces cuando lo haces
Matrix rotation = Matrix.CreateRotationZ(Math.PiOver2);
Realmente está creando un tipo de valor.
Esto se debe a que, a diferencia de la programación de aplicaciones, una pérdida de unos pocos milisegundos mientras el recolector de basura se ejecuta no es aceptable (¡solo obtenemos 16.6 ms para renderizar todo el fotograma!) , Así que debemos evitar las asignaciones tanto como sea posible para que el GC no tiene que correr tanto.
Esto es especialmente cierto 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!
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 para un diccionario.
También son útiles para PInvoke / interoperabilidad o escenarios de redes 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 realmente, nunca los uso yo mismo. No te preocupes por no usarlos.
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 deberían ser más grandes en la huella de memoria que 16 Bytes, porque si crecen, la CLR tiene que asignar memoria adicional.
Además, debido a que estructura ''en vivo'' en la pila (y no en el montón como tipos de referencia), puede considerar el uso de estructuras si necesita crear instancias de muchos de los mismos tipos de objetos.
Una estructura en C # es en esencia nada más ni nada menos que un conjunto de variables pegadas 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 uno desea usar 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 un pt
variable de tipo Immutable3dPoint
y uno 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 uno desea copiar los valores de la ubicación de almacenamiento P
a la ubicación de almacenamiento Q
, a otro, uno debe copiar todos los campos o propiedades de una instancia a la otra (tal vez haciendo que el tipo implemente un método CopyFrom
y diciendo Q.CopyFrom(P);
Tenga en cuenta que si uno dice Q=P;
puede parecer que funciona, pero los intentos futuros de modificar P
también modificarán Q
y viceversa. Las clases mutables pueden funcionar, y a veces pueden ser eficientes, pero es muy fácil 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 para copiar que las referencias a objetos inmutables, pero 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 de la estructura general. 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.
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.
Usé una estructura para representar una Geolocalización
struct LatLng
{
public decimal Lattitude
{
get;
set;
}
public decimal Longitude
{
get;
set;
}
}
esto representa una sola entidad, por ejemplo, puedo agregar 2 LatLng juntos o realizar otras operaciones en esta única entidad.
El tipo de estructura es adecuado para representar objetos livianos como Punto, Rectángulo y Color. Aunque es posible representar un punto como una clase, una estructura es más eficiente en algunos escenarios. Por 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 observas los tipos primitivos Int32, decimal, double ..etc, notarás que son todas las estructuras , lo que les permite ser tipos de valores al tiempo que les permite implementar ciertas interfaces cruciales.