Единицы измерения в C # - почти

Вдохновленный Единицы измерения в F #, и несмотря на утверждение (здесь), что вы не могли сделать это на 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);
    }
}

(Мне бы хотелось избежать создания экземпляров объектов с помощью static, но, как мы все знаем, вы не может объявить статический метод в интерфейсе). Затем вы можете сделать это:

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

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

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

ОБНОВЛЕНИЕ для людей, интересующихся этой предметной областью, здесь - это ссылка на статью 1997 года, в которой обсуждается другой тип решения (не специально для C #).


person Benjol    schedule 08.12.2008    source источник
comment
Взгляните на калькулятор Frink и язык программирования Frink.   -  person Kamil Szot    schedule 25.10.2010
comment
Интересно, подходил ли кто-нибудь к модулям на C # с атрибутами для значений свойств класса.   -  person John Alexiou    schedule 17.11.2010
comment
Фринк - бомба для такого рода проблем.   -  person justin.m.chase    schedule 21.09.2011
comment
Возможно, мне не хватает чего-то очевидного, но зачем вам нужна другая (т.е. отличная от F #) реализация единиц измерения на основе CLR? Или это просто так?   -  person pblasucci    schedule 21.09.2011
comment
@pblasucci, просто ради этого. (Чтобы быть разборчивым, реализация F # не основана на CLR, все это происходит в компиляторе, среда выполнения ничего не видит) ...   -  person Benjol    schedule 22.09.2011
comment
@Benjol: Конечно, справедливо по поводу компилятора. Я должен был сказать «пригоден для использования в среде CLR». Ваше здоровье!   -  person pblasucci    schedule 22.09.2011
comment
@ ja72: я использую атрибуты для объявления единиц, но это чисто для отображения. Я использую отражение, чтобы узнать, что это за единицы на самом деле, чтобы я мог преобразовать во все, что хочет пользовательский интерфейс. Было бы очень хорошо иметь возможность использовать эти аннотации для отлова попыток добавить яблоки и апельсины.   -  person Kevin Whitefoot    schedule 25.06.2014
comment
Для этого есть запрос функции для C #. На данный момент я думаю, что этот проект, QuantityTypes, справится с этой задачей.   -  person orad    schedule 23.10.2015


Ответы (14)


Вам не хватает анализа размеров. Например (из ответа, на который вы ссылаетесь) в 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
    {
    }
}

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

person Matthew Crumley    schedule 08.12.2008
comment
Да, я знал, что это то, чего не хватало, мне очень нравится ваше решение, но, как вы говорите, сейчас не время компиляции. Проголосуйте в любом случае. - person Benjol; 08.12.2008
comment
Мне не нравится видеть инициализаторы, которые усложняются, когда мы добавляем больше базовых модулей. Поскольку вы уже теряете возможность обнаруживать неправильные единицы во время компиляции, вы можете сделать еще один шаг и просто использовать словарь, отображающий строку или перечисление в int, вместо того, чтобы иметь отдельное поле для каждого типа. - person Brian; 01.05.2012
comment
В системе СИ всего 7 основных единиц (время, масса, длина, температура, сила света, количество вещества и электрический ток). Если вы добавите значение множителя к Unit, которая является фабрикой преобразования обратно в представление SI, вы можете получить довольно хорошую модель. - person Paul Hatcher; 17.10.2012

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

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

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

person Drew Noakes    schedule 01.06.2010

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

Length  length       = Length.FromMillimeters(n1);
decimal lengthInFeet = length.Feet;
Length  length2      = length.AddFeet(n2);
Length  length3      = length + Length.FromMeters(n3);
person Mark Cidade    schedule 08.12.2008
comment
Это тоже было моим первым инстинктом. Обратной стороной этого является то, что вам нужно явно определять операторы, которые объединяют все перестановки единиц. Это становится намного сложнее, когда вы начинаете комбинировать разные единицы вместе, такие как Velocity (Length / TimeSpan), когда вы используете очень большое количество преобразований FromXXX, которые вам нужно поддерживать. - person Chris Kerekes; 09.08.2011

Недавно я выпустил 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
person angularsen    schedule 21.07.2013

Сейчас существует такая библиотека C #: http://www.codeproject.com/Articles/413750/Units-of-Measure-Validator-for-Csharp

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

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

person Henning    schedule 03.07.2012
comment
Интересная попытка, но автор признает, что недоволен проектом, и предлагает начать все с нуля. Аналогичную библиотеку можно найти здесь: github.com/InitialForce/UnitsNet - person kmote; 08.11.2013

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

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

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

Кроме того, насколько успешно мы можем заставить компилятор обрабатывать модуль как обычный двойной, когда мы того пожелаем, без использования CType или ".Value" или каких-либо дополнительных обозначений? Например, с значениями NULL, код знает, как обрабатывать двойные? точно так же, как double (конечно, если ваш double? равен нулю, вы получите ошибку).

person Scott    schedule 09.02.2011
comment
Да, позже перейду к этому обсуждению, но именно поэтому я думаю, что это должна быть функция основного языка для помощи разработчикам, а не библиотека кода, чтобы в самом скомпилированном коде не осталось абстракций. - person Jonas; 14.01.2016

Спасибо за идею. Я реализовал модули на 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>
person John Alexiou    schedule 16.11.2010
comment
Не уверен, почему вы получили отрицательный голос, но я думаю, что вам, вероятно, будет лучше изучить F #, чем пытаться заново изобрести колесо. Я обновил свой вопрос ссылкой на статью, которая может вас заинтересовать. - person Benjol; 17.11.2010
comment
Спасибо за конструктивный комментарий. - person John Alexiou; 17.11.2010

есть jscience: http://jscience.org/, а вот отличный dsl для модулей: http://groovy.dzone.com/news/domain-specific-language-unit-. iirc, в C # есть замыкания, так что вы сможете что-то придумать.

person Ray Tayek    schedule 08.12.2008

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

person Jonathan C Dickinson    schedule 08.12.2008

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

http://quantitysystem.codeplex.com

person MovGP0    schedule 30.03.2014

Стоит ли пользоваться?

Да. Если у меня есть номер передо мной, я хочу знать, что это такое. В любое время дня. Кроме того, это то, что мы обычно делаем. Мы организуем данные в значимую сущность - класс, структуру, вы называете это. Удваивается в координаты, строки в имена и адреса и т. Д. Почему единицы должны быть другими?

Кто-то уже сделал лучше?

Зависит от того, как лучше определять. Есть несколько библиотек, но я их не пробовал, поэтому у меня нет мнения. К тому же это портит удовольствие от пробовать самому :)

Теперь о реализации. Я хотел бы начать с очевидного: бесполезно пытаться воспроизвести [<Measure>] систему F # в C #. Почему? Поскольку, как только F # позволяет вам использовать / ^ (или что-то еще в этом отношении) непосредственно с другим типом, игра проиграна. Удачи в выполнении этого на C # на struct или class. Уровень метапрограммирования, необходимый для такой задачи, не существует, и я боюсь, что он не будет добавлен в ближайшее время - на мой взгляд. Вот почему вам не хватает размерного анализа, о котором Мэтью Крамли упомянул в своем ответе.

Возьмем пример из fsharpforfunandprofit.com: у вас Newtons определены как [<Measure>] type N = kg m/sec^2. Теперь у вас есть функция square, созданная автором, которая возвращает N^2, что звучит неправильно, абсурдно и бесполезно. Если вы не хотите выполнять арифметические операции, когда в какой-то момент процесса оценки вы можете получить что-то бессмысленное, пока вы не умножите это на какую-то другую единицу и не получите значимый результат. Или, что еще хуже, вы можете захотеть использовать константы. Например, газовая постоянная R, которая равна 8.31446261815324 J /(K mol). Если вы определите соответствующие единицы, тогда F # готов использовать константу R. C # - нет. Вам нужно указать другой тип только для этого, и вы все равно не сможете выполнять какие-либо операции с этой константой.

Это не значит, что не стоит пытаться. Я сделал это и очень доволен результатами. Я начал SharpConvert около 3 лет назад, после того как меня вдохновил именно этот вопрос. Триггером послужила такая история: однажды мне пришлось исправить неприятную ошибку в разработанном мной симуляторе RADAR: самолет падал в земле вместо того, чтобы следовать заранее заданной глиссаде. Это не радовало меня, как вы могли догадаться, и после 2 часов отладки я понял, что где-то в своих расчетах я считал километры морскими милями. До этого момента я думал, что я просто буду «осторожен», что, по крайней мере, наивно для любой нетривиальной задачи.

В вашем коде было бы несколько вещей, которые я бы сделал по-другому.

Сначала я бы превратил реализации UnitDouble<T> и IUnit в структуры. Единица - это просто число, и если вы хотите, чтобы они рассматривались как числа, структура является более подходящим подходом.

Тогда я бы избегал new T() в методах. Он не вызывает конструктор, он использует Activator.CreateInstance<T>(), и для обработки чисел это будет плохо, так как добавит накладные расходы. Хотя это зависит от реализации, для простого приложения конвертера единиц это не повредит. Для критического по времени контекста избегайте чумы. И не поймите меня неправильно, я использовал его сам, так как не знал лучшего, и на днях я запустил несколько простых тестов, и такой вызов может удвоить время выполнения - по крайней мере, в моем случае. Подробнее см. Рассмотрение ограничения new () в C #: прекрасный пример дырявой абстракции

Я бы также изменил Convert<T, R>() и сделал его функцией-членом. Я предпочитаю писать

var c = new Unit<Length.mm>(123);
var e = c.Convert<Length.m>();

скорее, чем

var e = Length.Convert<Length.mm, Length.m>(c);

И последнее, но не менее важное: я бы использовал отдельные оболочки для каждой физической величины (время длины и т. Д.) Вместо UnitDouble, так как будет проще добавить функции, специфичные для физических величин, и перегрузки операторов. Это также позволит вам создать Speed<TLength, TTime> оболочку вместо другого Unit<T1, T2> или даже Unit<T1, T2, T3> класса. Так это выглядело бы так:

public readonly struct Length<T> where T : struct, ILength
{
    private static readonly double SiFactor = new T().ToSiFactor;
    public Length(double value)
    {
        if (value < 0) throw new ArgumentException(nameof(value));
        Value = value;
    }

    public double Value { get; }

    public static Length<T> operator +(Length<T> first, Length<T> second)
    {
        return new Length<T>(first.Value + second.Value);
    }

    public static Length<T> operator -(Length<T> first, Length<T> second)
    {
        // I don't know any application where negative length makes sense,
        // if it does feel free to remove Abs() and the exception in the constructor
        return new Length<T>(System.Math.Abs(first.Value - second.Value));
    }
    
    // You can add more like
    // public static Area<T> operator *(Length<T> x, Length<T> y)
    // or
    //public static Volume<T> operator *(Length<T> x, Length<T> y, Length<T> z)
    // etc

    public Length<R> To<R>() where R : struct, ILength
    {
        //notice how I got rid of the Activator invocations by moving them in a static field;
        //double mult = new T().ToSiFactor;
        //double div = new R().ToSiFactor;
        return new Length<R>(Value * SiFactor / Length<R>.SiFactor);
    }
}

Заметьте также, что, чтобы спасти нас от ужасного вызова Activator, я сохранил результат new T().ToSiFactor в SiFactor. Сначала это может показаться неудобным, но, поскольку Length является общим, Length<mm> будет иметь свою собственную копию, Length<Km> свою собственную, и так далее и тому подобное. Обратите внимание, что ToSiFactor - это toBase вашего подхода.

Проблема, которую я вижу, заключается в том, что пока вы находитесь в сфере простых единиц и вплоть до первой производной времени, все просто. Если вы попытаетесь сделать что-то более сложное, то увидите недостатки такого подхода. Набор текста

var accel = new Acceleration<m, s, s>(1.2);

не будет такой четкой и гладкой, как

let accel = 1.2<m/sec^2>

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

Последний недостаток (или преимущество, в зависимости от того, как вы его видите) этого дизайна заключается в том, что он не может быть независимым от единиц измерения. Если есть случаи, когда вам нужна только длина, вы не сможете ее получить. Каждый раз вам нужно знать, составляет ли ваша длина миллиметры, статутная миля или фут. Я использовал противоположный подход в SharpConvert, и LengthUnit происходит от UnitBase, а метры-километры и т. Д. Происходят от этого. Вот почему я, кстати, не мог пойти по пути struct. Таким образом вы получите:

LengthUnit l1 = new Meters(12);
LengthUnit l2 = new Feet(15.4);
LengthUnit sum = l1 + l2;

sum будет метрами, но это не должно волновать, если они хотят использовать его в следующей операции. Если они хотят отобразить это, они могут вызвать sum.To<Kilometers>() или любой другой модуль. Честно говоря, я не знаю, имеет ли отсутствие привязки переменной к конкретному модулю какие-либо преимущества. Возможно, в какой-то момент стоит исследовать это.

person Stelios Adamantidis    schedule 12.05.2021
comment
Вау! Это было путешествие по переулку памяти. Спасибо за интересный отзыв. Недавно у меня была аналогичная проблема, которую я решил (не очень элегантно) с типами - векторные вычисления для данных из источников с разными системами координат - в итоге я остановился на PointXY, PointYZ и PointXZ. Не очень красиво, но выявил несколько ошибок. - person Benjol; 17.05.2021
comment
@Benjol Ха-ха, надеюсь, у этого переулка остались хорошие воспоминания :) Да, он мог бы быть красивее, но на самом деле этого достаточно, если он уже обнаружил некоторые ошибки. - person Stelios Adamantidis; 26.05.2021

Я бы хотел, чтобы компилятор мне как можно больше помогал. Так что, возможно, вы могли бы иметь TypedInt, где T содержит фактическую единицу.

    public struct TypedInt<T>
    {
        public int Value { get; }

        public TypedInt(int value) => Value = value;

        public static TypedInt<T> operator -(TypedInt<T> a, TypedInt<T> b) => new TypedInt<T>(a.Value - b.Value);
        public static TypedInt<T> operator +(TypedInt<T> a, TypedInt<T> b) => new TypedInt<T>(a.Value + b.Value);
        public static TypedInt<T> operator *(int a, TypedInt<T> b) => new TypedInt<T>(a * b.Value);
        public static TypedInt<T> operator *(TypedInt<T> a, int b) => new TypedInt<T>(a.Value * b);
        public static TypedInt<T> operator /(TypedInt<T> a, int b) => new TypedInt<T>(a.Value / b);

        // todo: m² or m/s
        // todo: more than just ints
        // todo: other operations
        public override string ToString() => $"{Value} {typeof(T).Name}";
    }

У вас может быть расширенный метод для установки типа (или просто новый):

    public static class TypedInt
    {
        public static TypedInt<T> Of<T>(this int value) => new TypedInt<T>(value);
    }

Фактические единицы могут быть любыми. Таким образом, система становится расширяемой. (Есть несколько способов обработки конверсий. Как вы думаете, что лучше?)

    public class Mile
    {
        // todo: conversion from mile to/from meter
        // maybe define an interface like ITypedConvertible<Meter>
        // conversion probably needs reflection, but there may be
        // a faster way
    };

    public class Second
    {
    }

Таким образом, вы можете использовать:

            var distance1 = 10.Of<Mile>();
            var distance2 = 15.Of<Mile>();
            var timespan1 = 4.Of<Second>();

            Console.WriteLine(distance1 + distance2);
            //Console.WriteLine(distance1 + 5); // this will be blocked by the compiler
            //Console.WriteLine(distance1 + timespan1); // this will be blocked by the compiler
            Console.WriteLine(3 * distance1);
            Console.WriteLine(distance1 / 3);
            //Console.WriteLine(distance1 / timespan1); // todo!
person realbart    schedule 18.05.2021

См. Boo Ometa (который будет доступен для Boo 1.0): Boo Ometa и расширяемый синтаксический анализ

person justin.m.chase    schedule 28.01.2009
comment
ссылка: bamboo.github.com / 2008/08/05 / - person Igor Pashchuk; 21.09.2011
comment
исправил, извините, я не заметил этого раньше. - person justin.m.chase; 21.09.2011

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

У меня есть любимый проект, над которым я возился на протяжении многих лет, и недавно я начал его переписывать и выпустил в открытый исходный код по адресу https://github.com/MafuJosh/NGenericDimensions

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

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

Например:

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

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

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

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

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

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

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

person Mafu Josh    schedule 27.09.2011