假设我正在编写一个关于蛋糕的应用程序。
我需要存储蛋糕的重量(公斤)及其 FCR(糖霜/巧克力比率。我编的)。我可以将这些值存储为
float
。我看到的问题是我可以将重量(公斤)值分配给 FCR 字段。
C# 的类型系统可以防止此类错误。如果我创建一个
class WeightInKg
和一个 class FrostingChocolateRatio
,我将无法将其中一个分配给另一个。
然后我需要再次实现所有数字运算符(
+ - * / > < ==
等)。这些实现起来已经很烦人了,因为它们只是 float
功能的包装。然而,由于这些结构都是基于 float
的 both,所以所有这些包装器方法实际上都是相同的。对于所有其他此类基于
float
的类型来说,情况也是如此。
abstract class FloatValue
可以提供所有数值运算,但这些操作随后返回 FloatValue
类型而不是(类型安全)子类类型。我还觉得结构应该用于本质上是基本值的东西,并且结构不支持子类。struct Quantity<T>
已执行所有数值运算。对于 T
,我然后使用空的“标记类”,它仅用于识别某种类型的数量(例如 FrostingChocolateRatio
)。这是可行的,但不断使用 Quantity<WhatIReallyWant>
会很尴尬,并且会在代码中产生更多的视觉混乱。这是否最接近我想要的,或者是否有更干净的方法在 C# 中拥有此类类型安全值?
正如 PaulF 提到的,
FrostingChocolateRatio
不是一个理想的例子,因为比率的数学运算方式不同。然而,我今天已经没有创造力了;假设它与 WeightInKg
完全相同。float
完全相同,但在一升中添加一厘米是没有意义的。
今天又一时兴起发现了这个,哇,六年了——同时有两件事发生了变化。
首先是我作为开发者的经历。当时,将相同的代码复制粘贴到几个不同的部分感觉“错误”。有一个数值,例如无法分割感觉不完整。
然而,从我目前的观点来看......我们并不是试图编写一个功能完整的数字库。我们需要对 2 或 3 种不同类型的值进行一些操作。在需要的地方复制粘贴代码绝对足够了,也是解决这个问题最省时的方法。
第二部分是技术方面。在 .NET Core 中,默认的 .NET 数字类型现在使用一长串接口,每个接口都描述了可能操作的一小部分。这是在这种情况下挑选和选择实际需要的零件的完美选择:
public readonly struct UInt64 :
IComparable,
IConvertible,
ISpanFormattable,
IFormattable,
IComparable<ulong>,
IEquatable<ulong>,
IBinaryInteger<ulong>,
IBinaryNumber<ulong>,
IBitwiseOperators<ulong, ulong, ulong>,
INumber<ulong>,
IComparisonOperators<ulong, ulong, bool>,
IEqualityOperators<ulong, ulong, bool>,
IModulusOperators<ulong, ulong, ulong>,
INumberBase<ulong>,
IAdditionOperators<ulong, ulong, ulong>,
IAdditiveIdentity<ulong, ulong>,
IDecrementOperators<ulong>,
IDivisionOperators<ulong, ulong, ulong>,
IIncrementOperators<ulong>,
IMultiplicativeIdentity<ulong, ulong>,
IMultiplyOperators<ulong, ulong, ulong>,
ISpanParsable<ulong>,
IParsable<ulong>,
ISubtractionOperators<ulong, ulong, ulong>,
IUnaryPlusOperators<ulong, ulong>,
IUnaryNegationOperators<ulong, ulong>,
IUtf8SpanFormattable,
IUtf8SpanParsable<ulong>,
IShiftOperators<ulong, int, ulong>,
IMinMaxValue<ulong>,
IUnsignedNumber<ulong>,
IBinaryIntegerParseAndFormatInfo<ulong>