在C#中的度量单位 - 几乎 [英] Units of measure in C# - almost
问题描述
,尽管断言(<一href=\"http://stackoverflow.com/questions/39492/where-can-f-actually-save-time-and-money#71112\">here)你不能在C#中做到这一点,我有一个想法,有一天,我已经和玩弄。
Inspired by Units of Measure in F#, and despite asserting (here) that you couldn't do it in C#, I had an idea the other day which I've been playing around with.
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
下一步是尝试执行转换(片段):
The next step is trying to implement conversions (snippet):
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在接口声明一个静态方法)
然后,你可以这样做:
(I would have liked to avoid instantiating objects by using static, but as we all know you can't declare a static method in an interface) You can then do this:
var e = Length.Convert<Length.mm, Length.m>(c);
var f = Length.Convert<Length.mm, Mass.kg>(d); <-- but not this
显然,在这一个大洞,相比度量F#机组(我会让你的工作吧)
Obviously, there is a gaping hole in this, compared to F# Units of measure (I'll let you work it out).
呵呵,问题是:你认为这是什么?是否值得使用?已经有人已经做的更好?
Oh, the question is: what do you think of this? Is it worth using? Has someone else already done better?
更新作为对这个问题感兴趣地区的人们,这里从1997年讨论一种不同的解决方案(不包括专门为C#),以一纸链接
UPDATE for people interested in this subject area, here is a link to a paper from 1997 discussing a different kind of solution (not specifically for C#)
推荐答案
您缺少维度分析。例如(从您链接到的答案),在F#中,你可以这样做:
You are missing dimensional analysis. For example (from the answer you linked to), in F# you can do this:
let g = 9.8<m/s^2>
和它会产生加速度的新单位,从米,秒衍生(你其实可以做同样的事情在C ++中使用模板)。
and it will generate a new unit of acceleration, derived from meters and seconds (you can actually do the same thing in C++ using templates).
在C#中,可以在运行时做多维分析,但它的开销增加和不给你的编译时检查的好处。据我知道有没有办法做到完整的编译时间单元在C#。
In C#, it is possible to do dimensional analysis at runtime, but it adds overhead and doesn't give you the benefit of compile-time checking. As far as I know there's no way to do full compile-time units in C#.
不管是值得做的事情取决于课程的申请,但对于许多科学应用,这绝对是一个好主意。我不知道.NET任何现有的库,但他们可能存在的。
Whether it's worth doing depends on the application of course, but for many scientific applications, it's definitely a good idea. I don't know of any existing libraries for .NET, but they probably exist.
如果你有兴趣在如何在运行时做到这一点,这个想法是,每个值都有一个标值和整数重新presenting每个基本单元的电源。
If you are interested in how to do it at runtime, the idea is that each value has a scalar value and integers representing the power of each basic unit.
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
...;
}
}
如果您不允许用户更改单元的值(一个好主意,反正),你可以为普通单位添加子类:
If you don't allow the user to change the value of the units (a good idea anyways), you could add subclasses for common units:
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
{
}
}
您也可以定义在派生类型更具体的运营商,以避免检查是普通类型兼容的单位。
You could also define more specific operators on the derived types to avoid checking for compatible units on common types.
这篇关于在C#中的度量单位 - 几乎的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!