2008-12-08 2 views
52

Вдохновленный Units of Measure in F# и, несмотря на утверждение (here), что вы не могли сделать это на C#, у меня была идея на днях, с которой я играл. ИспользованиеЕдиницы измерения в C# - почти

namespace UnitsOfMeasure 
{ 
    public interface IUnit { } 
    public static class Length 
    { 
     public interface ILength : IUnit { } 
     public class m : ILength { } 
     public class mm : ILength { } 
     public class ft : ILength { } 
    } 
    public class Mass 
    { 
     public interface IMass : IUnit { } 
     public class kg : IMass { } 
     public class g : IMass { } 
     public class lb : IMass { } 
    } 

    public class UnitDouble<T> where T : IUnit 
    { 
     public readonly double Value; 
     public UnitDouble(double value) 
     { 
      Value = value; 
     } 
     public static UnitDouble<T> operator +(UnitDouble<T> first, UnitDouble<T> second) 
     { 
      return new UnitDouble<T>(first.Value + second.Value); 
     } 
     //TODO: minus operator/equality 
    } 
} 

Пример:

var a = new UnitDouble<Length.m>(3.1); 
var b = new UnitDouble<Length.m>(4.9); 
var d = new UnitDouble<Mass.kg>(3.4); 
Console.WriteLine((a + b).Value); 
//Console.WriteLine((a + c).Value); <-- Compiler says no 

Следующим шагом пытается реализовать Конверсии (фрагмент):

public interface IUnit { double toBase { get; } } 
public static class Length 
{ 
    public interface ILength : IUnit { } 
    public class m : ILength { public double toBase { get { return 1.0;} } } 
    public class mm : ILength { public double toBase { get { return 1000.0; } } } 
    public class ft : ILength { public double toBase { get { return 0.3048; } } } 
    public static UnitDouble<R> Convert<T, R>(UnitDouble<T> input) where T : ILength, new() where R : ILength, new() 
    { 
     double mult = (new T() as IUnit).toBase; 
     double div = (new R() as IUnit).toBase; 
     return new UnitDouble<R>(input.Value * mult/div); 
    } 
} 

(мне бы хотелось, чтобы избежать инстанцировании объектов с помощью статической, но как мы все знаем вас can't declare a static method in an interface) Затем вы можете сделать это:

var e = Length.Convert<Length.mm, Length.m>(c); 
var f = Length.Convert<Length.mm, Mass.kg>(d); <-- but not this 

Очевидно, что в этом есть зияющее отверстие, по сравнению с единицами измерения F # (я позволю вам разобраться).

О, вопрос в том, что вы думаете об этом? Стоит ли использовать? Кто-то еще уже сделал лучше?

UPDATE для людей, заинтересованных в данной предметной области, here является ссылка на документ с 1997 года обсуждают различного рода решения (не специально для C#)

+2

Посмотрите на Frink калькулятор и языка программирования Frink. – 2010-10-25 08:29:54

+2

Интересно, обратился ли кто-нибудь к единицам на C# с атрибутами значений класса. – ja72 2010-11-17 13:02:35

+0

Frink - это бомба для этого типа проблем. – 2011-09-21 14:59:09

ответ

14

Использование отдельных классов для разных единиц одной и той же меры (например, см, мм и ft для длины) кажутся странными. На основе классов DateTime и TimeSpan в .NET Framework, я бы ожидать что-то вроде этого:

Length length  = Length.FromMillimeters(n1); 
decimal lengthInFeet = length.Feet; 
Length length2  = length.AddFeet(n2); 
Length length3  = length + Length.FromMeters(n3); 
39

Вам не нужен анализ размеров. Например (из ответа вы связаны с), в F # вы можете сделать это:

let g = 9.8<m/s^2> 

и он будет генерировать новый блок ускорения, производный от метров и секунд (вы на самом деле можете сделать то же самое в C++ используя шаблоны).

В C# можно выполнить анализ размеров во время выполнения, но он добавляет служебные данные и не дает вам возможности проверять время компиляции. Насколько я знаю, в C# нет возможности делать полные блоки компиляции.

Независимо от того, стоит ли это делать, это зависит от применения, конечно, но для многих научных приложений это определенно хорошая идея. Я не знаю каких-либо существующих библиотек для .NET, но они, вероятно, существуют.

Если вы заинтересованы в том, как это сделать во время выполнения, идея состоит в том, что каждое значение имеет скалярное значение и целые числа, представляющие мощность каждого базового блока.

class Unit 
{ 
    double scalar; 
    int kg; 
    int m; 
    int s; 
    // ... for each basic unit 

    public Unit(double scalar, int kg, int m, int s) 
    { 
     this.scalar = scalar; 
     this.kg = kg; 
     this.m = m; 
     this.s = s; 
     ... 
    } 

    // For addition/subtraction, exponents must match 
    public static Unit operator +(Unit first, Unit second) 
    { 
     if (UnitsAreCompatible(first, second)) 
     { 
      return new Unit(
       first.scalar + second.scalar, 
       first.kg, 
       first.m, 
       first.s, 
       ... 
      ); 
     } 
     else 
     { 
      throw new Exception("Units must match for addition"); 
     } 
    } 

    // For multiplication/division, add/subtract the exponents 
    public static Unit operator *(Unit first, Unit second) 
    { 
     return new Unit(
      first.scalar * second.scalar, 
      first.kg + second.kg, 
      first.m + second.m, 
      first.s + second.s, 
      ... 
     ); 
    } 

    public static bool UnitsAreCompatible(Unit first, Unit second) 
    { 
     return 
      first.kg == second.kg && 
      first.m == second.m && 
      first.s == second.s 
      ...; 
    } 
} 

Если вы не позволяют пользователю изменять значение единицы (хорошая идея в любом случае), вы можете добавить подклассы для общих единиц:

class Speed : Unit 
{ 
    public Speed(double x) : base(x, 0, 1, -1, ...); // m/s => m^1 * s^-1 
    { 
    } 
} 

class Acceleration : Unit 
{ 
    public Acceleration(double x) : base(x, 0, 1, -2, ...); // m/s^2 => m^1 * s^-2 
    { 
    } 
} 

Вы можете также определить более конкретные операторы на производных типах, чтобы избежать проверки совместимых устройств на общие типы.

0

Почему бы не использовать CodeDom для автоматического создания всех возможных перестановок блоков? Я знаю, что это не самое лучшее - но я обязательно сработаю!

15

Вы можете добавить методы расширения числовых типов для создания мер. Было бы чувствовать себя немного DSL-как:

var mass = 1.Kilogram(); 
var length = (1.2).Kilometres(); 

Это не совсем .NET конвенции и не может быть наиболее обнаруживаемой особенностью, поэтому, возможно, вы бы добавить их в преданных именах для людей, которые им нравятся, а а также предлагает более традиционные методы строительства.

3

Спасибо за идею. Я реализовал единицы в C# разными способами, которые всегда кажутся уловкой. Теперь я могу попробовать еще раз, используя идеи, описанные выше. Моя цель состоит в том, чтобы иметь возможность определять новые блоки на основе существующих как

Unit lbf = 4.44822162*N; 
Unit fps = feet/sec; 
Unit hp = 550*lbf*fps 

и для программы, чтобы выяснить правильные размеры, масштабирование и символ для использования. В конце концов мне нужно построить базовую систему алгебр, которая может преобразовывать вещи, такие как (m/s)*(m*s)=m^2, и попытаться выразить результат на основе существующих единиц.

Также требование должно быть, чтобы иметь возможность сериализации блоки таким образом, что новые устройства не должны быть закодированы, но только объявленные в файле XML, как это:

<DefinedUnits> 
    <DirectUnits> 
<!-- Base Units --> 
<DirectUnit Symbol="kg" Scale="1" Dims="(1,0,0,0,0)" /> 
<DirectUnit Symbol="m" Scale="1" Dims="(0,1,0,0,0)" /> 
<DirectUnit Symbol="s" Scale="1" Dims="(0,0,1,0,0)" /> 
... 
<!-- Derived Units --> 
<DirectUnit Symbol="N" Scale="1" Dims="(1,1,-2,0,0)" /> 
<DirectUnit Symbol="R" Scale="1.8" Dims="(0,0,0,0,1)" /> 
... 
    </DirectUnits> 
    <IndirectUnits> 
<!-- Composite Units --> 
<IndirectUnit Symbol="m/s" Scale="1"  Lhs="m" Op="Divide" Rhs="s"/> 
<IndirectUnit Symbol="km/h" Scale="1"  Lhs="km" Op="Divide" Rhs="hr"/> 
... 
<IndirectUnit Symbol="hp" Scale="550.0" Lhs="lbf" Op="Multiply" Rhs="fps"/> 
    </IndirectUnits> 
</DefinedUnits> 
3

Вот моя забота с создание единиц в C#/VB. Пожалуйста, поправьте меня, если вы считаете, что я неправ. Большинство реализаций, о которых я читал, похоже, связаны с созданием структуры, которая объединяет значение (int или double) с единицей. Затем вы пытаетесь определить основные функции (+ - */и т. Д.) Для этих структур, которые учитывают конверсии единиц и согласованность.

Я считаю эту идею очень привлекательной, но каждый раз, когда я отказываюсь от какого-то огромного шага для проекта, это кажется. Это похоже на сделку «все или ничего». Вы, вероятно, не просто изменили бы несколько чисел на единицы; все дело в том, что все данные внутри проекта соответствующим образом помечены единицей, чтобы избежать какой-либо двусмысленности. Это означает попрощаться с обычными двойниками и ints, каждая переменная теперь определяется как «единица» или «длина» или «метры» и т. Д. Неужели люди действительно делают это в больших масштабах? Поэтому, даже если у вас большой массив, каждый элемент должен быть отмечен блоком. Очевидно, что это будет иметь как размеры, так и последствия.

Несмотря на все умения в попытке протолкнуть логику устройства в фоновом режиме, некоторые громоздкие обозначения кажутся неизбежными с C#. F # делает некоторые закулисные магии, которые лучше уменьшают коэффициент раздражения логики устройства.

Кроме того, насколько успешно мы можем заставить компилятор обрабатывать единицу, как обычный двойной, когда мы этого хотим, без использования CType или «.Value» или каких-либо дополнительных обозначений? Например, с нулевыми значениями, код знает, как обращаться с двойным? точно так же, как двойной (конечно, если ваш двойной?имеет значение null, то вы получите сообщение об ошибке).

0

Мне очень понравилось читать этот вопрос переполнения стека и его ответы.

У меня есть любимый проект, который я переделал на протяжении многих лет, и в последнее время стали вновь писать его и выпустили его с открытым исходным кодом на http://ngenericdimensions.codeplex.com

Это случается несколько похожа на многие идеи, выраженные в вопросе и ответах на этой странице.

В основном это создание общих размеров с единицей измерения и собственным типом данных в качестве заполнителей типового типа.

Например:

Dim myLength1 as New Length(of Miles, Int16)(123) 

С также некоторым дополнительным использованием методов расширения нравится:

Dim myLength2 = 123.miles 

И

Dim myLength3 = myLength1 + myLength2 
Dim myArea1 = myLength1 * myLength2 

Это не будет составлять:

Dim myValue = 123.miles + 234.kilograms 

Новые устройства могут быть расширены в ваших собственных библиотеках.

Эти типы данных представляют собой структуры, которые содержат только одну внутреннюю переменную, что делает их легкими.

В основном, перегрузки оператора ограничены структурами «измерения», поэтому каждая единица измерения не требует перегрузки оператора.

Конечно, большой недостаток - это более длинное объявление синтаксиса дженериков, для которого требуется 3 типа данных. Так что если это проблема для вас, то это не ваша библиотека.

Основная цель состояла в том, чтобы украсить интерфейс модулями во время проверки времени компиляции.

Существует много вещей, которые нужно сделать в библиотеке, но я хотел бы опубликовать их на случай, если это то, что кто-то ищет.

9

Теперь такое C# библиотека существует: http://www.codeproject.com/Articles/413750/Units-of-Measure-Validator-for-Csharp

Это почти те же функции, как и блок F # 's компиляции проверки времени, но для C#. Ядро - это задача MSBuild, которая анализирует код и ищет проверки.

Информация об устройстве хранится в комментариях и атрибутах.

8

Я недавно выпустил Units.NET на GitHub и на NuGet.

Это дает вам все общие единицы и конверсии. Он легковес, протестирован и поддерживает PCL.

Пример преобразования:

Length meter = Length.FromMeters(1); 
double cm = meter.Centimeters; // 100 
double yards = meter.Yards; // 1.09361 
double feet = meter.Feet; // 3.28084 
double inches = meter.Inches; // 39.3701 
0

вы могли бы использовать QuantitySystem вместо реализации его самостоятельно.Он основывается на F # и значительно улучшает управление блоками в F #. Это лучшая реализация, которую я нашел до сих пор и может использоваться в проектах C#.

http://quantitysystem.codeplex.com

Смежные вопросы