TL; DR - я пытаюсь разработать оптимальную структуру данных для определения единиц в единице измерения.
А Unit of measure
по существу является value
(или количеством), связанным с unit
. Единицы СИ имеют семь основ или размеров. А именно: длина, масса, время, электрический ток, температура, количество вещества (молей) и сила света.
Это было бы достаточно просто, но есть ряд производных единиц, а также ставки, которые мы часто используем. Примером объединенной единицы будет Ньютон: kg * m / s^2
и примерная скорость будет tons / hr
.
У нас есть приложение, которое сильно зависит от подразумеваемых единиц. Мы будем встраивать единицы в имя переменной или столбца. Но это создает проблемы, когда нам нужно указать единицу измерения с разными единицами. Да, мы можем преобразовывать значения при вводе и отображении, но это генерирует много служебного кода, который мы хотели бы инкапсулировать в своем собственном классе.
Существует множество решений для Codeplex и других сред совместной работы. Лицензирование для проектов приемлемо, но сам проект обычно оказывается слишком легким или слишком тяжелым. Мы гоняемся за нашим собственным единорогом "просто правильно".
В идеале я мог бы определить новую единицу измерения, используя что-то вроде этого:
UOM myUom1 = новая UOM (10, вольт);
UOM myUom2 = новая UOM (43,2, Ньютоны);
Конечно, мы используем сочетание единиц Imperial и SI в зависимости от потребностей наших клиентов.
Нам также необходимо поддерживать синхронизацию этой структуры модулей с будущей таблицей базы данных, чтобы мы могли обеспечить такую же степень согласованности и в наших данных.
Как лучше всего определить единицы, производные единицы и ставки, которые нам нужно использовать для создания класса наших единиц измерения? Я мог видеть использование одного или нескольких перечислений, но это может расстраивать других разработчиков. Единственное перечисление было бы огромным с 200+ записями, тогда как множественные перечисления могли бы сбивать с толку, основываясь на СИ против Имперских юнитов, и дополнительную разбивку на основе категоризации самого юнита.
Примеры Enum, показывающие некоторые из моих проблем:
myUnits.Volt
myUnits.Newton
myUnits.meterSIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs
Наш набор используемых единиц довольно хорошо определен, и это конечное пространство. Нам нужна возможность расширять и добавлять новые производные единицы или тарифы, когда у нас есть спрос на них со стороны клиентов. Проект находится на C #, хотя я думаю, что более широкие аспекты дизайна применимы к нескольким языкам.
Одна из библиотек, на которую я смотрел, позволяет вводить единицы в свободной форме через строку. Затем их класс UOM проанализировал строку и распределил ее соответственно. Сложность такого подхода заключается в том, что он заставляет разработчика задуматься и запомнить, какие правильные форматы строк. И я рискую ошибкой / исключением во время выполнения, если мы не добавим дополнительные проверки в коде для проверки строк, передаваемых в конструктор.
Другая библиотека создала слишком много классов, с которыми разработчику пришлось бы работать. Наряду с эквивалентной UoM он предоставил , DerivedUnit
и RateUnit
и так далее. По сути, код был слишком сложным для задач, которые мы решаем. Эта библиотека, по сути, допускает любые: любые комбинации (что является законным в мире единиц), но мы рады охватить нашу проблему (упростить наш код), не допуская каждую возможную комбинацию.
Другие библиотеки были смехотворно просты и даже не рассматривали перегрузку операторов, например.
Кроме того, я не так обеспокоен попытками неправильных преобразований (например: вольт в метры). Разработчики - единственные, кто получит доступ на этом уровне в данный момент, и нам не обязательно защищать от подобных ошибок.
Ответы:
Библиотеки Boost для C ++ включают в себя статью по анализу измерений, в которой представлен пример реализации обработки единиц измерения.
Подводя итог: Единицы измерения представлены в виде векторов, где каждый элемент вектора представляет фундаментальное измерение:
Производные единицы являются их комбинациями. Например, сила (масса * расстояние / время ^ 2) будет представлена как
Имперские и СИ единицы могут быть обработаны путем добавления коэффициента пересчета.
Эта реализация опирается на специфичные для C ++ методы (использующие шаблонное метапрограммирование для простого превращения разных единиц измерения в разные типы времени компиляции), но концепции следует перенести на другие языки программирования.
источник
mpl::vector_c<int,1,0,0,0,0,0,0>
) вместо consts; В статье вначале представлен подход к принципу «против» (и я, вероятно, не очень хорошо это объяснил). Использование consts будет работать как альтернатива (вы потеряете некоторую безопасность типов во время компиляции). Использование пространства имен, чтобы избежать загрязнения имен, безусловно, вариант.Я только что выпустил Units.NET на Github и NuGet .
Это дает вам все общие единицы и преобразования. Это легкий, испытанный блок и поддерживает PCL.
На ваш вопрос:
Мне еще предстоит увидеть Святой Грааль решений в этой области. Как вы заявляете, он может стать слишком сложным или многословным для работы. Иногда лучше сохранять простоту, и для моих нужд такой подход оказывается достаточным.
Явное преобразование
Динамическое преобразование
источник
Truple<T1, T2, T3>(x, y, z)
Tuple
. Я не могу видеть вашUnitConverter
класс, но IMO кажется, что он может иметь схожую функциональность сTuple
классом.Если вы можете переключиться на F # вместо использования C #, F # имеет систему единиц измерения (реализованную с использованием метаданных о значениях), которая выглядит так, как будто она соответствует тому, что вы пытаетесь сделать:
http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure
В частности:
источник
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.
Исходя из того, что все необходимые преобразования являются масштабными (за исключением случаев, когда вам необходимо поддерживать температурные преобразования. Расчеты, в которых преобразование включает смещение, значительно сложнее), я бы разработал свою систему «единиц измерения» следующим образом:
Класс,
unit
содержащий коэффициент масштабирования, строку для текстового представления блока и ссылку, на которуюunit
масштабируется. Текстовое представление предназначено для отображения и ссылки на базовую единицу, чтобы узнать, в какой единице получается результат при выполнении математических операций со значениями с разными единицами.Для каждого поддерживаемого модуля предоставляется статический экземпляр
unit
класса.Класс,
UOM
содержащий значение и ссылку на значениеunit
.UOM
Класс предоставляет перегруженные операторы для добавления / вычитания другогоUOM
и для умножения / деления со значением безразмерного.Если сложение / вычитание выполняется на двух
UOM
одинаковыхunit
, оно выполняется напрямую. В противном случае оба значения преобразуются в соответствующие базовые единицы и добавляются / вычитаются. Результат сообщается как находящийся в базеunit
.Использование будет как
Поскольку операции с несовместимыми модулями не считаются проблемой, я не пытался сделать конструкцию безопасной в этом отношении. Можно добавить проверку времени выполнения, проверив, что два блока относятся к одному базовому блоку.
источник
95F - 85F
? Что такое20C - 15C
? В обоих примерах обаUOM
s будут одинаковымиunit
. Будут ли вычитания выполняться напрямую?10 F
и5 C
. Расчеты выполняются напрямую, если это возможно, чтобы избежать ненужных преобразований. Было бы довольно тривиально добавить методы преобразования единиц измеренияUOM
, но для преобразования Цельсия-Фаренгейтаunit
класс должен быть расширен с возможностью смещения в дополнение к коэффициенту масштабирования.95F - 85F
! =10F
.95F
на85F
? Насколько мне известно, по Фаренгейту все еще линейная шкала.20C - 15C = 5C
, то мы говорим293.15K - 288.15K = 278.15K
, что явно неправильно.Подумайте о том, что делает ваш код и что он позволит. Имея простое перечисление со всеми возможными единицами в нем, я могу сделать что-то вроде преобразования Вольт в метры. Это явно не относится к человеку, но программное обеспечение с удовольствием попробует.
Однажды я сделал нечто похожее на это, и в моей реализации были абстрактные базовые классы (длина, вес и т. Д.), Которые все реализовали
IUnitOfMeasure
. Каждый абстрактный базовый класс определяет тип по умолчанию (классLength
имеет реализацию класса по умолчаниюMeter
), которую он будет использовать для всех преобразований. Следовательно,IUnitOfMeasure
реализованы два разных метода,ToDefault(decimal)
иFromDefault(decimal)
.Фактическое число, которое я хотел обернуть, было универсальным типом, принимающим в
IUnitOfMeasure
качестве универсального аргумента. Сказав что-то вроде,Measurement<Meter>(2.0)
вы получаете автоматическую безопасность типов. Реализация правильных неявных преобразований и математических методов в этих классах позволяет вам делать подобные вещиMeasurement<Meter>(2.0) * Measurement<Inch>(12)
и возвращать результат в типе по умолчанию (Meter
). Я никогда не работал с производными единицами, такими как Ньютоны; Я просто оставил их как Килограмм * Метр / Секунду / Секунду.источник
Я считаю, что ответ лежит в ответе MarioVW на переполнение стека :
У меня была аналогичная потребность в моем приложении.
Tuple
также является неизменным, что также справедливо для таких объектов, как веса и меры ... Как говорится, «пинта фунт мир вокруг».источник
Мой прототип кода: http://ideone.com/x7hz7i
Мои дизайнерские очки:
источник
В журнале есть хорошая статья, по-немецки: http://www.dotnetpro.de/articles/onlinearticle1398.aspx
Основная идея состоит в том, чтобы иметь класс Unit, например Length, с BaseMeasurement. Класс содержит коэффициент преобразования, операторные перегрузки, перегрузки ToString, синтаксический анализатор строк и реализацию в качестве индексатора. Мы даже реализовали даже архитектурное представление, но оно не выпущено как библиотека.
Итак, вы видите использование с оператором давления или просто:
Но, как вы сказали, я не нашел и единорога :)
источник
Это смысл команды Unix
units
, которая делает все это, используя подход, основанный на файлах данных, для определения отношений.источник
units
. Основная причина, по которой модули не будут работать для моего более широкого решения - это строки произвольной формы. Конечно, он возвращает сообщения об ошибках, но этот подход нацелен на разработчиков, которые будут интегрировать этот код с нашим приложением. Строки произвольной формы предоставляют слишком много возможностей для ошибок.units
файл данных. То, как оно определяет отношения между количествами, очень чисто и может быть полезно для вашей проблемы.