TL; DR : estoy tratando de diseñar una estructura de datos óptima para definir unidades dentro de una unidad de medida.
A Unit of measure
es esencialmente una value
(o cantidad) asociada con a unit
. Las unidades SI tienen siete bases o dimensiones. A saber: longitud, masa, tiempo, corriente eléctrica, temperatura, cantidad de sustancia (moles) e intensidad luminosa.
Esto sería bastante sencillo, pero hay una serie de unidades derivadas, así como tasas que usamos con frecuencia. Una unidad combinada de ejemplo sería el Newton: kg * m / s^2
y una tasa de ejemplo sería tons / hr
.
Tenemos una aplicación que depende en gran medida de las unidades implícitas. Incorporaremos las unidades dentro del nombre de la variable o columna. Pero esto crea problemas cuando necesitamos especificar una unidad de medida con diferentes unidades. Sí, podemos convertir los valores en la entrada y en la pantalla, pero esto genera muchos códigos generales que nos gustaría encapsular dentro de su propia clase.
Existen varias soluciones en codeplex y otros entornos colaborativos. La licencia para los proyectos es aceptable, pero el proyecto en sí mismo generalmente termina siendo demasiado ligero o demasiado pesado. Estamos persiguiendo a nuestro propio unicornio de "justo".
Idealmente, podría definir una nueva unidad de medida usando algo como esto:
UOM myUom1 = nueva UOM (10, voltios);
UOM myUom2 = nueva UOM (43.2, Newtons);
Por supuesto, utilizamos una combinación de unidades imperiales y SI basadas en las necesidades de nuestros clientes.
También necesitamos mantener esta estructura de unidades sincronizadas con una tabla de base de datos futura para que podamos proporcionar el mismo grado de consistencia dentro de nuestros datos también.
¿Cuál es la mejor manera de definir las unidades, las unidades derivadas y las tasas que necesitamos usar para crear nuestra clase de unidad de medida? Pude ver el uso de una o más enumeraciones, pero eso podría ser frustrante para otros desarrolladores. Una sola enumeración sería enorme con más de 200 entradas, mientras que varias enumeraciones podrían ser confusas en función de las unidades SI frente a las imperiales y un desglose adicional basado en la categorización de la unidad en sí.
Ejemplos de Enum que muestran algunas de mis preocupaciones:
myUnits.Volt
myUnits.Newton
myUnits.meterSIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs
Nuestro conjunto de unidades en uso está bastante bien definido y es un espacio finito. Necesitamos la capacidad de expandir y agregar nuevas unidades derivadas o tasas cuando tengamos la demanda de los clientes. El proyecto está en C #, aunque creo que los aspectos de diseño más amplios son aplicables a múltiples lenguajes.
Una de las bibliotecas que miré permite la entrada de unidades de forma libre a través de una cadena. Su clase de UOM luego analizó la cadena y las cosas ranuradas en consecuencia. El desafío con este enfoque es que obliga al desarrollador a pensar y recordar cuáles son los formatos de cadena correctos. Y corro el riesgo de un error / excepción de tiempo de ejecución si no agregamos verificaciones adicionales dentro del código para validar las cadenas que se pasan en el constructor.
Otra biblioteca esencialmente creó demasiadas clases con las que el desarrollador tendría que trabajar. Junto con un equivalente UOM proporcionó una DerivedUnit
y RateUnit
etcétera. Esencialmente, el código era demasiado complejo para los problemas que estamos resolviendo. Esa biblioteca esencialmente permitiría cualquiera: cualquier combinación (que es legítima en el mundo de las unidades), pero nos complace analizar nuestro problema (simplificar nuestro código) al no permitir todas las combinaciones posibles.
Otras bibliotecas eran ridículamente simples y ni siquiera habían considerado la sobrecarga del operador, por ejemplo.
Además, no estoy tan preocupado por los intentos de conversiones incorrectas (por ejemplo: voltios a metros). Los desarrolladores son los únicos que tendrán acceso a este nivel en este momento y no necesariamente tenemos que protegernos contra ese tipo de errores.
Respuestas:
Las bibliotecas Boost para C ++ incluyen un artículo sobre análisis dimensional que presenta una implementación de muestra de unidades de medida de manejo.
Para resumir: las unidades de medida se representan como vectores, y cada elemento del vector representa una dimensión fundamental:
Las unidades derivadas son combinaciones de estos. Por ejemplo, la fuerza (masa * distancia / tiempo ^ 2) se representaría como
Las unidades imperiales versus SI podrían manejarse agregando un factor de conversión.
Esta implementación se basa en técnicas específicas de C ++ (usando metaprogramación de plantillas para convertir fácilmente diferentes unidades de medida en diferentes tipos de tiempo de compilación), pero los conceptos deben transferirse a otros lenguajes de programación.
fuente
mpl::vector_c<int,1,0,0,0,0,0,0>
) en lugar de consts; El artículo presenta el enfoque de los concursos primero a modo de explicación (y probablemente no lo expliqué bien). El uso de consts funcionaría como una alternativa (perderías algo de seguridad de tipo de tiempo de compilación). Usar un espacio de nombres para evitar la contaminación de nombres es ciertamente una opción.Acabo de lanzar Units.NET en Github y en NuGet .
Te da todas las unidades y conversiones comunes. Es liviano, probado en unidades y compatible con PCL.
Hacia su pregunta:
Todavía tengo que ver el santo grial de soluciones en este dominio. Como usted dice, puede fácilmente volverse demasiado complejo o demasiado detallado para trabajar. A veces, es mejor mantener las cosas simples y para mis necesidades, este enfoque ha demostrado ser suficiente.
Conversión explícita
Conversión dinámica
fuente
Truple<T1, T2, T3>(x, y, z)
Tuple
. No puedo ver tuUnitConverter
clase, pero en mi opinión, parece que puede compartir una funcionalidad similar a laTuple
clase.Si puede cambiar a F # en su lugar usando C #, F # tiene un sistema de unidades de medida (implementado usando metadatos en los valores) que parece que se ajustará a lo que está tratando de hacer:
http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure
Notablemente:
fuente
Important: Units of measure look like a data type, but they aren't. .NET's type system does not support the behaviors that units of measure have, such as being able to square, divide, or raise datatypes to powers. This functionality is provided by the F# static type checker at compile time, **but units are erased from compiled code**. Consequently, it is not possible to determine value's unit at runtime.
Basado en el hecho de que todas las conversiones requeridas son conversiones de escala (excepto si tiene que soportar conversiones de temperatura. Los cálculos donde la conversión implica un desplazamiento son significativamente más complejos), diseñaría mi sistema de 'unidad de medida' de esta manera:
Una clase que
unit
contiene un factor de escala, una cadena para la representación textual de la unidad y una referencia a la que seunit
escala. La representación textual está allí para fines de visualización y la referencia a la unidad base para saber en qué unidad se encuentra el resultado al hacer cálculos matemáticos de valores con diferentes unidades.Para cada unidad compatible, se proporciona una instancia estática de la
unit
clase.Una clase que
UOM
contiene un valor y una referencia a los valoresunit
. LaUOM
clase proporciona operadores sobrecargados para sumar / restar otroUOM
y para multiplicar / dividir con un valor adimensional.Si la suma / resta se realiza en dos
UOM
con el mismounit
, se realiza directamente. De lo contrario, ambos valores se convierten a sus respectivas unidades base y se suman / restan. El resultado se informa como estar en la baseunit
.El uso sería como
Como las operaciones en unidades incompatibles no se consideran un problema, no he tratado de hacer que el diseño sea seguro en ese sentido. Es posible agregar una verificación de tiempo de ejecución al verificar que dos unidades se refieren a la misma unidad base.
fuente
95F - 85F
? ¿Qué es20C - 15C
? En ambos ejemplos, ambosUOM
s tendrían lo mismounit
. ¿Se realizarían las restas directamente?10 F
y5 C
. Los cálculos se realizan directamente si es posible, para evitar conversiones innecesarias. Sería bastante trivial agregar métodos de conversión de unidadesUOM
, pero para la conversión Celsius-Fahrenheit, launit
clase debería extenderse con la posibilidad de un desplazamiento además de un factor de escala.95F - 85F
! =10F
.95F
por85F
? Que yo sepa, Fahrenheit sigue siendo una escala lineal.20C - 15C = 5C
, entonces decimos293.15K - 288.15K = 278.15K
, lo cual es claramente incorrecto.Piensa en lo que está haciendo tu código y lo que permitirá. Tener una enumeración simple con todas las unidades posibles en él me permite hacer algo como convertir voltios a metros. Obviamente, eso no es válido para un humano, pero el software lo intentará con gusto.
Hice algo vagamente similar a esto una vez, y mi implementación tenía clases base abstractas (longitud, peso, etc.) que todas implementaron
IUnitOfMeasure
. Cada clase de bases abstractas definió un tipo predeterminado (la claseLength
tenía una implementación predeterminada de la claseMeter
) que usaría para todo el trabajo de conversión. Por lo tanto,IUnitOfMeasure
implementó dos métodos diferentes,ToDefault(decimal)
yFromDefault(decimal)
.El número real que quería ajustar era un tipo genérico que aceptaba
IUnitOfMeasure
como argumento genérico. Decir algo asíMeasurement<Meter>(2.0)
te da seguridad de tipo automático. Implementar las conversiones implícitas y los métodos matemáticos adecuados en estas clases le permite hacer cosas comoMeasurement<Meter>(2.0) * Measurement<Inch>(12)
y devolver un resultado en el tipo predeterminado (Meter
). Nunca trabajé unidades derivadas como Newtons; Simplemente los dejé como kilogramo * metro / segundo / segundo.fuente
Creo que la respuesta se encuentra en la respuesta de Stack Overflow de MarioVW a:
Tenía una necesidad similar para mi aplicación.
Tuple
También es inmutable, lo que también es cierto para los objetos como Pesos y Medidas ... Como dice el refrán "un litro por libra al mundo".fuente
Mi código de prototipo: http://ideone.com/x7hz7i
Mis puntos de diseño:
fuente
Hay un buen artículo en una revista que se encuentra en alemán: http://www.dotnetpro.de/articles/onlinearticle1398.aspx
La idea base es tener una clase de Unidad como Longitud con una Medición Base. La clase contiene el factor de conversión, las sobrecargas del operador, las sobrecargas de ToString, el analizador de cadenas y una implementación como indexador. Incluso hemos implementado incluso la vista Architectual, pero no se publica como una biblioteca.
Entonces puede ver el uso con el operador de Presión o simplemente:
Pero como dijiste, tampoco encontré el unicornio :)
fuente
Esta es la razón de ser del
units
comando Unix , que lo hace todo utilizando un enfoque basado en archivos de datos para especificar las relaciones.fuente
units
. La razón principal por la que las unidades no funcionarán para mi solución más amplia son las cadenas de forma libre. Por supuesto, proporciona mensajes de error a cambio, pero este enfoque está dirigido a desarrolladores que integrarán este código con nuestra aplicación. Las cadenas de forma libre presentan demasiadas oportunidades de error.units
archivo de datos de. La forma en que define las relaciones entre cantidades es muy clara y puede ser útil para su problema.