适用于 .NET 的度量单位库






4.93/5 (7投票s)
本文介绍了一个用于处理度量单位的库。
引言
该库提供了几个用于处理度量单位的类,并尽可能接近 SI 标准。它能够从 XML 加载单位定义、解析单位、转换单位并进行计算。支持像“km/h”这样的派生单位,并且可以通过将“km”除以“h”动态构建它们。除了像“Fahrenheit”和“Celsius”这样的偏移单位外,还可以添加自定义单位。附带的演示项目演示了如何从 Internet 导入自更新的货币单位。因此,您可以使用最新的汇率将“l/EUR”转换为“cm^3/USD”。
由于库本身已提供完整的 XML 文档,因此我将只介绍要点。
该库是 C# 度量单位验证器 的一部分。
单位的概念
有三种不同类型的单位:基本单位、比例/偏移单位和派生单位。
每个单位都有一个可选的名称和一个缩写。当单位注册到单位解析器时,以及对于基本单位,需要缩写,因为缩写定义了基本单位的身份。
基本单位 不能被缩短或由其他单位构建。例如“米”或“秒”。两个基本单位相等,当且仅当它们的缩写相等。
比例/偏移单位 由一个因子、一个偏移量和一个底层单位组成。例如“千米”,它等于 1000“米”。底层单位可以是任何单位类型,因此您可以将单位“小时”定义为 60 分钟,将分钟定义为 60 秒。两个比例/偏移单位相等,当且仅当它们具有相同的相干单位以及相对于该相干单位的相同因子和偏移量。因此,“30 分钟”等于“半小时”,考虑到“分钟”被注册为“60 秒”,“半小时”被注册为“0.5 小时”,“30 分钟”或“1800 秒”——这三个选项都是可能的。偏移量对于非比例单位(如华氏度和摄氏度)是必需的。华氏度和摄氏度的相干单位是开尔文。
派生单位 是由多个部分组成的复合单位,每个部分由任意类型的单位和一个指数组成。例如“km/h”,它等于“km^1 * h^-1”或“liter”,它等于“dm^3”。如果所有部分都是相干的(除了比例/偏移单位部分),则派生单位是相干的:如果一个比例/偏移单位是非比例的(即偏移量不为 0),则其相干单位(仅用于派生单位)是最底层的具有相同偏移量的单位。因此,cm/µ°C(厘米每微摄氏度)的相干单位是 m/°C,而不是 m/°K。这是因为无法从 m/°C 中的数量 a 转换为 m/°K 中的数量 b(参见“偏移单位与派生单位的连接”一章)。
特殊情况是基本单位 **无量纲单位**,其缩写为“1”。无量纲单位是中性元素,因此任何单位与此特殊单位相乘或相除都等于该单位本身。无量纲单位除以另一个单位,是另一个单位的倒数(例如 1/m)。
所有单位都继承自 Unit
类,无量纲单位由静态只读的 Unit.Dimensionless
属性表示。
可以使用 Unit.Pow(int)
、*
和 /
对单位进行指数运算、乘法和除法。
转换
QuantityTransformation
描述了从一个单位的值到另一个单位的值的转换。两个转换可以连接起来,如果第一个转换的目标单位等于第二个转换的源单位。这种连接的转换描述了从第一个单位到最后一个单位的转换。
使用 Reverse
,可以反转每个转换,从而交换源单位和目标单位。从米到千米的转换将返回从千米到米的转换。
LinearQuantityTransformation
类是 QuantityTransformation
的一种,它描述了线性转换,例如“Transform(value) = Factor * value + Offset
”。
两个转换相等,当且仅当它们的目标单位相等且数学转换相同。两个非基本单位相等,当且仅当它们到其相干单位的转换相等。
什么是相干单位?
相干单位在与相干单位的关系表示中不需要任何因子(或偏移量)。所有基本单位都是相干的。
例如,牛顿的定义
,
所有这些单位都是相干的,因此牛顿也是相干的。
可以定义 1 牛顿' 为
,
但由于克不是相干单位,因此此定义不相干,并且包含任何因子的定义也同样不相干,例如
.
尽管如此,仍有可能通过乘以 1000 将 1 牛顿' 转换为相干牛顿。这样,度量单位库就可以确定 N' 和 N'' 之间的相等性。
解析单位
通过不同的单位解析器支持多种格式,每个解析器都实现了 IUnitParser
。如果解析失败,将根据 throwFormatException
参数返回 null
或抛出格式化异常。
注册单位解析器
此解析器实现了 IUnitParser
和 IUnitRegistry
,并使用字符串-单位字典查找给定字符串的相应单位。默认情况下,“1”作为无量纲单位已注册。
前缀单位解析器
此解析器能够通过遍历 Prefix
类中的所有可用前缀并将其底层单位(例如“km”的“m”,“THz”的“Hz”)的解析委托给另一个解析器(例如注册单位解析器)来解析像“km”或“THz”这样的字符串。
比例偏移单位解析器
比例偏移单位解析器解析由因子和单位缩写组成的 string
(例如“60 s”,即 1 分钟的定义)。单位缩写通过注入的 IUnitParser
进行解析。建议使用一个复合单位解析器,其中包含注册单位解析器和前缀单位解析器。
派生单位解析器
使用此解析器,可以解析像“m^2 * s^-1 / kg^2 * K”这样的单位表达式。
空格是可选的,如果指数等于 1,则指数也是可选的。斜杠“/”后面的所有指数都会被反转。虽然在数学上不正确,但没有括号看起来更清晰(除非您在评论中说服我)。
像比例偏移单位解析器一样,缩写的解析会委托给注入的 IUnitParser
。同样建议使用一个复合单位解析器,其中包含注册单位解析器和前缀单位解析器。
复合单位解析器
复合单位解析器聚合多个 IUnitParser
实例并返回第一个成功的结果。这样,所有提到的解析器都可以组合成一个。
GetUnitRegistry
方法返回第一个实现 IUnitRegistry
接口的实例(如果不存在这样的实例则返回 null
),因此可以快速将新单位注册到复合单位解析器中。
static
方法 NewDefaultUnitParser
返回一个完全可用的单位解析器,其中包含上述所有解析器。
缓存单位解析器
此解析器将解析委托给注入的单位解析器并缓存结果(缓存不会自动清空)。
从 XML 加载单位定义
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="Celsius" Abbr="°F"
Factor="0.55555555555555555555" Offset="255.37222222222222222222"
UnderlayingUnit="K" />
<DerivedUnit Name="Meters per Second" Abbr="mps">
<UnitPart Unit="m" Exponent="1" />
<!-- the definition for m and s is left out in this snippet -->
<UnitPart Unit="s" Exponent="-1" />
</DerivedUnit>
</UnitLibrary>
线程安全
原则上,使用此库是线程安全的,因为每个 Unit
都是不可变的,或者对成员的访问是同步的。解析单位也是线程安全的,全局单位解析器也是如此。
演示
附带的 C# 演示项目演示了如何使用默认单位并从 Internet 导入自更新的货币单位。您可以输入一个值、一个源单位和一个目标单位。如果可以进行转换,则会显示结果。
偏移单位与派生单位的连接
通常,从一个单位 (x) 到底层单位 (f) 的转换可以通过函数 f(x) 来表示。
比例/偏移单位通过以下方式描述:
.
如果一个单位的偏移量不为 0,则该单位是非比例的。当多个单位组合在一起时,这会成为一个问题。
假设 g(z)
.
如果给定的值 v 是单位 x/z(1v = 1x/z),则其转换为单位 f/g(1v' = 1f/g)中的 v' 可以描述为
.
只有当 x 和 z 已知或偏移量为 0 时,才能计算 v'。
假设偏移量为零(如果单位 x 是比例的)
.
因此,如果非比例单位与派生单位结合使用,则无法将其转换为其相干等价物。尽管如此,µ°C(微摄氏度)可以转换为 °C,因此 °C 是 µ°C 的相干单位(注解:独立使用时,从 µ°C 到 K 的转换仍然可能)。
为此,引入了 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。
C° 是开尔文的别名,因此也是一个相干单位。
历史
- 1.1 一些更新
- 1.0 初始版本