TL; DR - Ich versuche, eine optimale Datenstruktur zu entwerfen, um Einheiten innerhalb einer Maßeinheit zu definieren.
A Unit of measure
ist im Wesentlichen eine value
(oder eine Menge), die mit a assoziiert ist unit
. SI-Einheiten haben sieben Basen oder Dimensionen. Nämlich: Länge, Masse, Zeit, elektrischer Strom, Temperatur, Substanzmenge (Mol) und Lichtstärke.
Dies wäre recht einfach, aber es gibt eine Reihe von abgeleiteten Einheiten sowie Raten, die wir häufig verwenden. Eine kombinierte Beispieleinheit wäre der Newton: kg * m / s^2
und eine Beispielrate wäre tons / hr
.
Wir haben eine Anwendung, die sich stark auf implizite Einheiten stützt. Wir werden die Einheiten in den Variablen- oder Spaltennamen einbetten. Dies schafft jedoch Probleme, wenn wir eine Maßeinheit mit verschiedenen Einheiten angeben müssen. Ja, wir können die Werte bei der Eingabe und Anzeige konvertieren, aber dies generiert eine Menge Overhead-Code, den wir in einer eigenen Klasse einkapseln möchten.
Es gibt eine Reihe von Lösungen für Codeplex und andere kollaborative Umgebungen. Die Lizenzierung für die Projekte ist akzeptabel, aber das Projekt selbst ist in der Regel zu leicht oder zu schwer. Wir jagen unser eigenes Einhorn "genau richtig".
Idealerweise könnte ich eine neue Maßeinheit folgendermaßen definieren:
UOM myUom1 = neue UOM (10, Volt);
UOM myUom2 = neue UOM (43,2, Newton);
Natürlich verwenden wir eine Mischung aus imperialen und SI-Einheiten, die auf die Bedürfnisse unserer Kunden abgestimmt ist.
Wir müssen diese Einheitenstruktur auch mit einer zukünftigen Datenbanktabelle synchronisieren, damit wir auch in unseren Daten den gleichen Grad an Konsistenz gewährleisten können.
Wie lassen sich die Einheiten, abgeleiteten Einheiten und Kurse am besten definieren, die zum Erstellen unserer Maßeinheitenklasse verwendet werden müssen? Ich könnte eine oder mehrere Aufzählungen verwenden, aber das könnte für andere Entwickler frustrierend sein. Eine einzelne Aufzählung würde mit mehr als 200 Einträgen sehr umfangreich sein, während mehrere Aufzählungen auf der Grundlage von SI-Einheiten gegenüber imperialen Einheiten und einer zusätzlichen Aufschlüsselung auf der Grundlage der Kategorisierung der Einheit selbst verwirrend sein könnten.
Aufzählungsbeispiele, die einige meiner Bedenken aufzeigen:
myUnits.Volt
myUnits.Newton
myUnits.meterSIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs
Unser Satz von Einheiten ist ziemlich gut definiert und es ist ein begrenzter Raum. Wir müssen in der Lage sein, neue abgeleitete Einheiten oder Raten zu erweitern und hinzuzufügen, wenn die Kundennachfrage danach besteht. Das Projekt ist in C #, obwohl ich denke, dass die umfassenderen Designaspekte auf mehrere Sprachen anwendbar sind.
Eine der Bibliotheken, die ich mir angesehen habe, ermöglicht die formlose Eingabe von Einheiten per String. Ihre UOM-Klasse analysierte dann die Zeichenfolge und schlitzte die Dinge entsprechend ein. Die Herausforderung bei diesem Ansatz besteht darin, dass der Entwickler gezwungen ist, über die richtigen Zeichenfolgenformate nachzudenken und sich diese zu merken. Und ich riskiere einen Laufzeitfehler / eine Laufzeitausnahme, wenn wir keine zusätzlichen Prüfungen im Code hinzufügen, um die im Konstruktor übergebenen Zeichenfolgen zu überprüfen.
Eine andere Bibliothek hat im Wesentlichen zu viele Klassen erstellt, mit denen der Entwickler arbeiten müsste. Zusammen mit einer äquivalenten UOM vorausgesetzt , es ist ein DerivedUnit
und RateUnit
und so weiter. Im Wesentlichen war der Code für die von uns gelösten Probleme zu komplex. Diese Bibliothek würde im Wesentlichen jede: beliebige Kombination zulassen (was in der Einheitenwelt legitim ist), aber wir sind glücklich, unser Problem zu erfassen (unseren Code zu vereinfachen), indem wir nicht jede mögliche Kombination zulassen.
Andere Bibliotheken waren lächerlich einfach und hatten zum Beispiel nicht einmal eine Überladung der Bediener in Betracht gezogen.
Außerdem mache ich mir weniger Sorgen über Versuche, falsche Umrechnungen vorzunehmen (zum Beispiel: Volt in Meter). Entwickler sind die einzigen, auf die auf dieser Ebene zu diesem Zeitpunkt zugegriffen werden kann, und wir müssen uns nicht unbedingt vor solchen Fehlern schützen.
Antworten:
Die Boost-Bibliotheken für C ++ enthalten einen Artikel zur Dimensionsanalyse , in dem eine Beispielimplementierung von Handling-Maßeinheiten vorgestellt wird.
Zusammenfassend: Maßeinheiten werden als Vektoren dargestellt, wobei jedes Element des Vektors eine grundlegende Dimension darstellt:
Abgeleitete Einheiten sind Kombinationen davon. Zum Beispiel würde Kraft (Masse * Distanz / Zeit ^ 2) dargestellt als
Imperial versus SI-Einheiten können durch Hinzufügen eines Umrechnungsfaktors behandelt werden.
Diese Implementierung basiert auf C ++ - spezifischen Techniken (mithilfe der Template-Metaprogrammierung können verschiedene Maßeinheiten leicht in verschiedene Typen zur Kompilierungszeit umgewandelt werden). Die Konzepte sollten jedoch auf andere Programmiersprachen übertragen werden.
quelle
mpl::vector_c<int,1,0,0,0,0,0,0>
) anstelle von Konstanten dargestellt. Der Artikel stellt zunächst den consts-Ansatz zur Erklärung vor (und das habe ich wahrscheinlich nicht gut erklärt). Die Verwendung von consts würde alternativ funktionieren (Sie würden beim Kompilieren einige Sicherheitseinstellungen verlieren). Die Verwendung eines Namespaces zur Vermeidung von Namensverschmutzung ist sicherlich eine Option.Ich habe gerade Units.NET auf Github und NuGet veröffentlicht .
Sie erhalten alle gängigen Einheiten und Umrechnungen. Es ist leicht, einheitengetestet und unterstützt PCL.
Zu Ihrer Frage:
Ich habe den heiligen Gral der Lösungen in diesem Bereich noch nicht gesehen. Wie Sie sagen, kann es leicht zu komplex oder zu ausführlich werden, um damit zu arbeiten. Manchmal ist es am besten, die Dinge einfach zu halten, und für meine Bedürfnisse hat sich dieser Ansatz als ausreichend erwiesen.
Explizite Konvertierung
Dynamische Konvertierung
quelle
Truple<T1, T2, T3>(x, y, z)
Tuple
. Ich kann IhreUnitConverter
Klasse nicht sehen , aber IMO sieht es so aus, als ob sie eine ähnliche Funktionalität wie dieTuple
Klasse hat.Wenn Sie stattdessen mit C # zu F # wechseln können, verfügt F # über ein Maßeinheitensystem (das mithilfe von Metadaten zu Werten implementiert wird), das Ihren Vorstellungen entspricht:
http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure
Vor allem:
quelle
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.
Basierend auf der Tatsache, dass alle erforderlichen Konvertierungen Skalierungskonvertierungen sind (außer wenn Sie Temperaturkonvertierungen unterstützen müssen. Berechnungen, bei denen die Konvertierung einen Versatz beinhaltet, sind wesentlich komplexer), würde ich mein "Maßeinheit" -System folgendermaßen entwerfen:
Eine Klasse
unit
mit einem Skalierungsfaktor, einer Zeichenfolge für die Textdarstellung der Einheit und einer Referenz, die dieunit
skaliert wird. Die Textdarstellung dient zu Anzeigezwecken und der Bezugnahme auf die Basiseinheit, um zu wissen, in welcher Einheit sich das Ergebnis befindet, wenn mit Werten mit verschiedenen Einheiten gerechnet wird.Für jede unterstützte Einheit wird eine statische Instanz der
unit
Klasse bereitgestellt.Eine Klasse,
UOM
die einen Wert und einen Verweis auf den Wert enthältunit
. DieUOM
Klasse bietet überladene Operatoren zum Addieren / Subtrahieren eines anderenUOM
und zum Multiplizieren / Dividieren mit einem dimensionslosen Wert.Wenn Addition / Subtraktion für zwei
UOM
mit demselben Wert ausgeführt wirdunit
, wird dies direkt ausgeführt. Andernfalls werden beide Werte in ihre jeweiligen Basiseinheiten konvertiert und addiert / subtrahiert. Das Ergebnis wird als in der Basis angegebenunit
.Nutzung wäre wie
Da Vorgänge mit nicht kompatiblen Einheiten kein Problem darstellen, habe ich nicht versucht, das Design in dieser Hinsicht typsicher zu machen. Sie können eine Laufzeitprüfung hinzufügen, indem Sie sicherstellen, dass sich zwei Einheiten auf dieselbe Basiseinheit beziehen.
quelle
95F - 85F
? Was ist20C - 15C
? In beiden Beispielen hätten beideUOM
s dasselbeunit
. Würden die Subtraktionen direkt ausgeführt?10 F
und5 C
. Die Berechnungen werden nach Möglichkeit direkt durchgeführt, um nicht benötigte Konvertierungen zu vermeiden. Es wäre ziemlich trivial, EinheitenumrechnungsmethodenUOM
zur Celsius-Fahrenheit-Umrechnung hinzuzufügen , aber für die Celsius-Fahrenheit-Umrechnungunit
müsste die Klasse um die Möglichkeit eines Versatzes zusätzlich zu einem Skalierungsfaktor erweitert werden.95F - 85F
! =10F
.95F
um senken85F
? Meines Wissens ist Fahrenheit immer noch eine lineare Skala.20C - 15C = 5C
, dann sagen wir293.15K - 288.15K = 278.15K
, was eindeutig falsch ist.Überlegen Sie, was Ihr Code tut und was er zulässt. Wenn ich eine einfache Aufzählung mit allen möglichen Einheiten habe, kann ich so etwas wie Volt in Meter umrechnen. Das ist natürlich für einen Menschen nicht gültig, aber die Software wird es gerne versuchen.
Ich habe einmal etwas Ähnliches getan, und meine Implementierung hatte abstrakte Basisklassen (Länge, Gewicht usw.), die alle implementiert wurden
IUnitOfMeasure
. Jede abstrakte Basisklasse definierte einen Standardtyp (KlasseLength
hatte eine Standardimplementierung von classMeter
), den sie für alle Konvertierungsarbeiten verwenden würde. DaherIUnitOfMeasure
wurden zwei verschiedene Methoden implementiert,ToDefault(decimal)
undFromDefault(decimal)
.Die tatsächliche Zahl, die ich einschließen wollte, war ein generischer Typ, der
IUnitOfMeasure
als generisches Argument akzeptiert wurde . Wenn Sie so etwas sagen, erhaltenMeasurement<Meter>(2.0)
Sie eine automatische Schriftsicherheit. Wenn Sie die richtigen impliziten Konvertierungen und mathematischen Methoden für diese Klassen implementieren, können Sie Folgendes tunMeasurement<Meter>(2.0) * Measurement<Inch>(12)
und ein Ergebnis mit dem Standardtyp (Meter
) zurückgeben. Ich habe niemals abgeleitete Einheiten wie Newton erarbeitet. Ich habe sie einfach als Kilogramm * Meter / Sekunde / Sekunde belassen.quelle
Ich glaube, die Antwort liegt in der Stapelüberlauf- Antwort von MarioVW auf:
Ich hatte ein ähnliches Bedürfnis nach meiner Bewerbung.
Tuple
ist auch unveränderlich, was auch für Objekte wie Maße und Gewichte zutrifft.quelle
Mein Prototypcode: http://ideone.com/x7hz7i
Meine Designpunkte:
quelle
Es gibt einen guten Artikel in einem Magazin, leider in deutscher Sprache: http://www.dotnetpro.de/articles/onlinearticle1398.aspx
Die Grundidee ist, eine Einheitenklasse wie Length mit einem BaseMeasurement zu haben. Die Klasse enthält den Konvertierungsfaktor, Operatorüberladungen, ToString-Überladungen, Parser von Zeichenfolgen und eine Implementierung als Indexer. Wir haben sogar die Architectual-Ansicht implementiert, diese ist jedoch nicht als Bibliothek verfügbar.
So sehen Sie die Verwendung mit dem Druckoperator oder nur:
Aber wie du sagtest, ich habe das Einhorn auch nicht gefunden :)
quelle
Dies ist das Grundprinzip des Unix-
units
Befehls, der alles unter Verwendung eines datendateigesteuerten Ansatzes zur Angabe der Beziehungen ausführt.quelle
units
. Der Hauptgrund, warum Einheiten für meine umfassendere Lösung nicht funktionieren, sind die Zeichenfolgen in freier Form. Zugegeben, es werden im Gegenzug Fehlermeldungen angezeigt. Dieser Ansatz zielt jedoch auf Entwickler ab, die diesen Code in unsere Anwendung integrieren. Freiform-Strings bieten zu viele Möglichkeiten für Fehler.units
. Die Art und Weise, wie Beziehungen zwischen Mengen definiert werden, ist sehr sauber und kann für Ihr Problem nützlich sein.