在科学和工程领域,单位的精确测量和转换至关重要。为了满足这一需求,开发了一套C#库,它能够处理各种单位,并且尽可能地接近国际单位制(SI)标准。该库支持从XML文件加载单位定义,解析单位,转换单位,并进行单位计算。它支持派生单位如"公里/小时",并且可以通过将单位"公里"除以"小时"动态构建。此外,该库还支持像"华氏度"和"摄氏度"这样的偏移单位,甚至可以添加自定义单位。附带的演示项目展示了如何从互联网导入自动更新的货币单位。因此,可以使用最新的汇率将"升/欧元"转换为"立方厘米/美元"。
库中定义了三种类型的单位:基本单位、缩放/偏移单位和派生单位。每个单位都有一个可选的名称和缩写。缩写对于注册到单位解析器的单位以及基本单位是必需的,因为缩写定义了基本单位的身份。
基本单位不能缩短,也不能由其他单位构成。例如"米"或"秒"。如果两个基本单位的缩写相同,则它们相等。
缩放/偏移单位由一个因子、一个偏移量和一个底层单位组成。例如"公里",它是1000个"米"。底层单位可以是任何类型的单位,因此可以定义"小时"为60分钟,一分钟为60秒。如果两个缩放/偏移单位具有相同的一致单位以及相对于这个一致单位的相同因子和偏移量,则它们相等。因此,"30分钟"等于"半小时",考虑到"分钟"被注册为"60秒","半小时"为"0.5小时","30分钟"或"1800秒" - 所有三种选项都是可能的。偏移量对于像华氏度或摄氏度这样的非比例单位是必需的。华氏度和摄氏度的一致单位是开尔文。
派生单位是由多个部分组成的复合单位,每个部分由任何类型的单位和一个指数组成。例如"公里/小时",它与"公里^1 * 小时^-1"或"升",它与"分米^3"相同。如果所有部分都是一致的,除了缩放/偏移单位部分:如果一个缩放/偏移单位是非比例的(即偏移量不为0),则其一致单位是(但仅对于派生单位)具有相同偏移量的最底层单位。因此,厘米/微摄氏度(厘米每微摄氏度)的一致单位是米/摄氏度,而不是米/开尔文。这是因为从数量a在米/摄氏度到b在米/开尔文的转换是不可能的(参见"派生单位中的偏移单位"章节)。
特殊案例是基本单位无量纲单位,其缩写为"1"。无量纲单位是一个中性元素,所以任何单位乘以或除以这个特殊单位都是该单位本身。无量纲单位除以另一个单位,是另一个单位的倒数(例如1/米)。
所有单位都继承自Unit类,无量纲单位由静态只读属性Unit.Dimensionless表示。
QuantityTransformation描述了一个值从一个单位到另一个单位的转换。如果第一个转换的目标单位与第二个转换的源单位相等,则可以链接两个转换。这样的链接转换描述了从最初的单位到最后一个单位的转换。
使用Reverse,每个转换都可以反转,使得源单位和目标单位交换。从米到公里的转换返回从公里到米的转换。
LinearQuantityTransformation类是一个QuantityTransformation,描述了线性转换,如"Transform(value) = Factor * value + Offset"。如果两个转换的目标单位相等,并且数学转换相同,则两个转换相等。如果两个非基本单位转换到它们的一致单位相等,则它们相等。
一致单位不需要任何因子(或偏移量)来表达与其他一致单位的关系。所有基本单位都是一致的。
例如,牛顿的定义:1牛顿 = 1千克 * 米/秒^2,所有这些单位都是一致的,因此牛顿也是一致的。
可以定义1牛顿'为1千克 * 米/秒^2 * 1000,但由于克不是一致单位,这个定义不是一致的,以及包含任何因子的定义如1牛顿' = 1千克 * 米/秒^2 * 1000。尽管如此,仍然可以将1牛顿'转换为一致的牛顿,通过乘以1000。这样,单位测量库可以确定N'和N''之间的等式。
通过不同的单位解析器支持多种格式,每个解析器都实现了IUnitParser。如果解析失败,将返回null或抛出格式异常,具体取决于throwFormatException参数。
这个解析器实现了IUnitParser和IUnitRegistry,并使用字符串-单位字典来找到给定字符串对应的单位。默认情况下注册了无量纲单位"1"。
这个解析器能够解析像"km"或"THz"这样的字符串,通过遍历Prefix类中的所有可用前缀,并将底层单位("km"的"m"或"THz"的"Hz")的解析委托给另一个解析器(例如注册单位解析器)。
缩放偏移单位解析器解析由因子和单位缩写组成的字符串(例如"60 s",1分钟的定义)。单位缩写通过注入的IUnitParser解析。建议使用组合单位解析器,包含注册单位解析器和前缀单位解析器。
这个解析器可以解析像"m^2 * s^-1 / kg^2 * K"这样的单位表达式。空格是可选的,如果指数等于1,则指数也是可选的。从"/"开始的每个指数都是反转的。尽管数学上不正确,但看起来更清晰,没有括号(除非在评论中说服相反)。
缩写的解析委托给注入的IUnitParser,就像缩放偏移单位解析器一样。同样建议使用组合单位解析器,包含注册单位解析器和前缀单位解析器。
组合单位解析器聚合多个IUnitParser实例,并返回第一个成功结果。这样,所有提到的解析器可以组合成一个。
GetUnitRegistry方法返回第一个实现IUnitRegistry接口的实例(如果没有这样的实例,则为null),因此可以快速将新单位注册到组合单位解析器中。
静态方法NewDefaultUnitParser返回一个包含上述所有解析器的完全可用的单位解析器。
这个解析器将解析委托给注入的单位解析器,并缓存结果(缓存不会自动清空)。
XmlUnitLibrary类解析XML文档,并将单位加载到IUnitRegistry中。XML文档必须符合HDUnitsOfMeasure项目的源代码中可以找到的XSD架构。
一般来说,XML文档看起来像这样:
<?xml version="1.0" encoding="utf-8"?>
<UnitLibrary xmlns="http://www.hediet.de/xsd/unitlibrary/1.0">
<BaseUnit Name="Kelvin" Abbr="K"/>
<ScaledShiftedUnit Name="Celsius" Abbr="°C" Factor="1" Offset="273.15" UnderlayingUnit="K"/>
<ScaledShiftedUnit Name="Fahrenheit" Abbr="°F" Factor="0.55555555555555555555" Offset="255.37222222222222222222" UnderlayingUnit="K"/>
<DerivedUnit Name="Meters per Second" Abbr="mps">
<UnitPart Unit="m" Exponent="1"/>
<UnitPart Unit="s" Exponent="-1"/>
</DerivedUnit>
</UnitLibrary>
原则上,使用这个库是线程安全的,因为每个Unit是不可变的,或者对成员的访问是同步的。解析单位也是线程安全的,以及使用全局单位解析器。
附带的C#演示项目展示了如何使用默认单位,并从互联网导入自动更新的货币单位。可以输入一个值,一个源单位和一个目标单位。如果可能进行转换,结果将显示出来。
一般来说,从一个单位(x)到底层(f)的转换可以通过函数f(x)来表示。缩放/偏移单位通过偏移量来描述。如果偏移量不为0,则该单位是非比例的。当多个单位组合在一起时,这就成了问题。
假设g(z):。如果给定一个值v在单位x/z(1v = 1x/z)中,其转换到v'在单位f/g(1v' = 1f/g)中可以描述为:。只有当x和z被给出或偏移量为0时,v'才能被计算。
假设偏移量为零(如果单位x是比例的):。所以如果非比例单位与派生单位一起使用,它们不能转换为它们的一致等效物。尽管如此,微摄氏度(微摄氏度)可以转换为摄氏度,因此摄氏度是微摄氏度的一致单位,如果它是派生单位的一部分(注释:单独,从微摄氏度到开尔文的转换仍然可能)。
为此,引入了接口ICouldBeUnproportional。实现这个接口的单位提供了方法,以获得与"一致"单位的比例转换。原则上,甚至不允许添加两个具有偏移量的单位:
1 °C + 1 °C = 274 K + 274 K = 548 K = 275 °C。
为了解决这个问题,可以使用C°,它描述了摄氏度的差异:
1 °C + 1 C° = 274 K + 1 K = 275 K = 2 °C。