TL; DR - J'essaie de concevoir une structure de données optimale pour définir les unités au sein d'une unité de mesure.
A Unit of measure
est essentiellement une value
(ou quantité) associée à a unit
. Les unités SI ont sept bases ou dimensions. À savoir: longueur, masse, temps, courant électrique, température, quantité de substance (moles) et intensité lumineuse.
Ce serait assez simple, mais il existe un certain nombre d'unités dérivées ainsi que des taux que nous utilisons fréquemment. Un exemple d'unité combinée serait le Newton: kg * m / s^2
et un exemple de taux serait tons / hr
.
Nous avons une application qui dépend fortement des unités implicites. Nous incorporons les unités dans le nom de la variable ou de la colonne. Mais cela crée des problèmes lorsque nous devons spécifier une unité de mesure avec différentes unités. Oui, nous pouvons convertir les valeurs en entrée et afficher mais cela génère beaucoup de code de surcharge que nous aimerions encapsuler dans sa propre classe.
Il existe un certain nombre de solutions sur le codeplex et d'autres environnements collaboratifs. Les licences pour les projets sont agréables mais le projet lui-même finit généralement par être trop léger ou trop lourd. Nous chassons notre propre licorne de "juste ce qu'il faut".
Idéalement, je pourrais définir une nouvelle unité de mesure en utilisant quelque chose comme ceci:
UOM myUom1 = nouvelle UOM (10, volts);
UOM myUom2 = nouvelle UOM (43.2, Newtons);
Bien sûr, nous utilisons un mélange d'unités impériales et SI en fonction des besoins de nos clients.
Nous devons également garder cette structure d'unités synchronisée avec une future table de base de données afin que nous puissions également fournir le même degré de cohérence dans nos données.
Quelle est la meilleure façon de définir les unités, les unités dérivées et les taux que nous devons utiliser pour créer notre classe d'unités de mesure? Je pouvais voir utiliser une ou plusieurs énumérations, mais cela pourrait être frustrant pour d'autres développeurs. Une énumération unique serait énorme avec plus de 200 entrées, tandis que plusieurs énumérations pourraient être source de confusion en fonction des unités SI vs impériales et d'une ventilation supplémentaire en fonction de la catégorisation de l'unité elle-même.
Enum exemples montrant certaines de mes préoccupations:
myUnits.Volt
myUnits.Newton
myUnits.meterSIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs
Notre ensemble d'unités utilisées est assez bien défini et c'est un espace fini. Nous avons besoin de pouvoir étendre et ajouter de nouvelles unités ou taux dérivés lorsque nous avons la demande des clients pour eux. Le projet est en C # bien que je pense que les aspects de conception plus larges sont applicables à plusieurs langues.
L'une des bibliothèques que j'ai examinées permet la saisie sous forme libre d'unités via une chaîne. Leur classe UOM a ensuite analysé la chaîne et placé les choses en conséquence. Le défi de cette approche est qu'elle oblige le développeur à réfléchir et à se souvenir des formats de chaîne corrects. Et je court le risque d'une erreur / exception d'exécution si nous n'ajoutons pas de vérifications supplémentaires dans le code pour valider les chaînes transmises dans le constructeur.
Une autre bibliothèque a essentiellement créé trop de classes avec lesquelles le développeur devrait travailler. En plus d'un UOM équivalent , il a fourni un DerivedUnit
et RateUnit
et ainsi de suite. Essentiellement, le code était trop complexe pour les problèmes que nous résolvons. Cette bibliothèque permettrait essentiellement n'importe quelle: n'importe quelle combinaison (ce qui est légitime dans le monde des unités) mais nous sommes heureux d'étendre notre problème (simplifier notre code) en n'autorisant pas toutes les combinaisons possibles.
D'autres bibliothèques étaient ridiculement simples et n'avaient même pas envisagé la surcharge de l'opérateur par exemple.
De plus, je ne suis pas aussi inquiet des tentatives de conversions incorrectes (par exemple: volts en mètres). Les développeurs sont les seuls à accéder à ce niveau à ce stade et nous n'avons pas nécessairement besoin de nous protéger contre ce type d'erreurs.
Réponses:
Les bibliothèques Boost pour C ++ incluent un article sur l'analyse dimensionnelle qui présente un exemple d'implémentation de la gestion des unités de mesure.
Pour résumer: Les unités de mesure sont représentées comme des vecteurs, chaque élément du vecteur représentant une dimension fondamentale:
Les unités dérivées sont des combinaisons de celles-ci. Par exemple, la force (masse * distance / temps ^ 2) serait représentée comme
Les unités impériales par rapport aux unités SI peuvent être traitées en ajoutant un facteur de conversion.
Cette implémentation repose sur des techniques spécifiques au C ++ (en utilisant la métaprogrammation de modèle pour transformer facilement différentes unités de mesure en différents types de compilation), mais les concepts devraient être transférés vers d'autres langages de programmation.
la source
mpl::vector_c<int,1,0,0,0,0,0,0>
) au lieu de consts; l'article présente d'abord l'approche consts à titre d'explication (et je n'ai probablement pas bien expliqué cela). L'utilisation de consts fonctionnerait comme une alternative (vous perdriez une certaine sécurité de type à la compilation). L'utilisation d'un espace de noms pour éviter la pollution par les noms est certainement une option.Je viens de publier Units.NET sur Github et sur NuGet .
Il vous donne toutes les unités et conversions courantes. Il est léger, testé à l'unité et prend en charge PCL.
Vers votre question:
Je n'ai pas encore vu le Saint Graal des solutions dans ce domaine. Comme vous le dites, cela peut facilement devenir trop complexe ou trop verbeux pour être utilisé. Parfois, il vaut mieux garder les choses simples et pour mes besoins, cette approche s'est avérée suffisante.
Conversion explicite
Conversion dynamique
la source
Truple<T1, T2, T3>(x, y, z)
Tuple
. Je ne peux pas voir votreUnitConverter
classe, mais il semble que l'OMI partage une fonctionnalité similaire à celle de laTuple
classe.Si vous pouvez tirer le passage à F # à la place en utilisant C #, F # a un système d'unités de mesure (implémenté à l'aide de métadonnées sur les valeurs) qui semble correspondre à ce que vous essayez de faire:
http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure
Notamment:
la source
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.
Sur la base du fait que toutes les conversions requises sont des conversions d'échelle (sauf si vous devez prendre en charge les conversions de température. Les calculs où la conversion implique un décalage sont beaucoup plus complexes), je concevoir mon système d'unité de mesure comme ceci:
Une classe
unit
contenant un facteur d'échelle, une chaîne de représentation textuelle de l'unité et une référence que launit
échelle. La représentation textuelle est là à des fins d'affichage et la référence à l'unité de base pour savoir dans quelle unité se trouve le résultat lors du calcul des valeurs avec des unités différentes.Pour chaque unité prise en charge, une instance statique de la
unit
classe est fournie.Une classe
UOM
contenant une valeur et une référence à la valeurunit
. LaUOM
classe fournit des opérateurs surchargés pour ajouter / soustraire un autreUOM
et pour multiplier / diviser avec une valeur sans dimension.Si l'addition / soustraction est effectuée sur deux
UOM
avec le mêmeunit
, elle est effectuée directement. Sinon, les deux valeurs sont converties en leurs unités de base respectives et ajoutées / soustraites. Le résultat est signalé comme étant dans la baseunit
.L'utilisation serait comme
Comme les opérations sur des unités incompatibles ne sont pas considérées comme un problème, je n'ai pas essayé de sécuriser le type de conception à cet égard. Il est possible d'ajouter un contrôle d'exécution en vérifiant que deux unités se réfèrent à la même unité de base.
la source
95F - 85F
? Qu'est-ce que c'est20C - 15C
? Dans les deux exemples, les deuxUOM
s auraient la même choseunit
. Les soustractions seraient-elles effectuées directement?10 F
et5 C
. Les calculs sont effectués directement si possible, pour éviter les conversions inutiles. Il serait assez trivial d'ajouter des méthodes de conversion d'unité àUOM
, mais pour la conversion Celsius-Fahrenheit, launit
classe devrait être étendue avec la possibilité d'un décalage en plus d'un facteur d'échelle.95F - 85F
! =10F
.95F
près85F
? À ma connaissance, Fahrenheit est toujours une échelle linéaire.20C - 15C = 5C
, alors nous disons293.15K - 288.15K = 278.15K
, ce qui est clairement faux.Réfléchissez à ce que fait votre code et à ce qu'il permettra. Avoir une énumération simple avec toutes les unités possibles me permet de faire quelque chose comme convertir des Volts en mètres. Ce n'est évidemment pas valable pour un humain, mais le logiciel se fera un plaisir d'essayer.
J'ai fait quelque chose de vaguement similaire à cela une fois, et mon implémentation avait des classes de base abstraites (longueur, poids, etc.) que toutes implémentaient
IUnitOfMeasure
. Chaque classe de bases abstraites a défini un type par défaut (la classeLength
avait une implémentation par défaut de classeMeter
) qu'elle utiliserait pour tous les travaux de conversion. D'où laIUnitOfMeasure
mise en œuvre de deux méthodes différentes,ToDefault(decimal)
etFromDefault(decimal)
.Le nombre réel que je voulais encapsuler était un type générique acceptant
IUnitOfMeasure
comme argument générique. Dire quelque chose commeMeasurement<Meter>(2.0)
vous donne une sécurité de type automatique. L'implémentation des conversions implicites et des méthodes mathématiques appropriées sur ces classes vous permet de faire des choses commeMeasurement<Meter>(2.0) * Measurement<Inch>(12)
et de retourner un résultat dans le type par défaut (Meter
). Je n'ai jamais travaillé sur des unités dérivées comme Newtons; Je les ai simplement laissés en kilogrammes * mètre / seconde / seconde.la source
Je pense que la réponse réside dans la réponse de MarioVW Stack Overflow à:
J'avais un besoin similaire pour ma candidature.
Tuple
est également immuable, ce qui est également vrai pour des objets tels que les poids et mesures… Comme le dit le proverbe "une pinte une livre dans le monde entier".la source
Mon code prototype: http://ideone.com/x7hz7i
Mes points de design:
la source
Il y a un bon article dans un magazine sans que ce soit en allemand: http://www.dotnetpro.de/articles/onlinearticle1398.aspx
L'idée de base est d'avoir une classe d'unité comme la longueur avec une mesure de base. La classe contient le facteur de conversion, les surcharges d'opérateur, les surcharges ToString, l'analyseur de chaînes et une implémentation en tant qu'indexeur. Nous avons même implémenté la vue architecturale, mais elle n'est pas publiée en tant que bibliothèque.
Vous voyez donc l'utilisation avec l'opérateur de pression ou simplement:
Mais comme vous l'avez dit, je n'ai pas non plus trouvé la licorne :)
la source
C'est la raison d'être de la
units
commande Unix , qui fait tout en utilisant une approche basée sur un fichier de données pour spécifier les relations.la source
units
. La principale raison pour laquelle les unités ne fonctionneront pas pour ma solution plus large est les chaînes de forme libre. Certes, il fournit des messages d'erreur en retour, mais cette approche cible les développeurs qui intégreront ce code à notre application. Les chaînes de forme libre présentent trop de risques d'erreur.units
fichier de données de. La façon dont il définit les relations entre les quantités est très propre et peut être utile à votre problème.