【微软技术栈】C#.NET 泛型数学

本文内.NET 7 为基类库引入了新的数学相关泛型接口。 提供这些接口意味着可以将泛型类型或方法的类型参数约束为“类似于数字”。 此外,C# 11 及更高版本允许定义 static virtual 接口成员。 由于必须将运算符声明为 static,因此这一新的 C# 功能可用于在新接口中为类似于数字的类型声明运算符。

总之,这些创新使你可以常规地执行数学运算,也就是说,无需知道正在使用的确切类型。 例如,如果想编写一个将两个数字相加的方法,在以前,必须为每种类型添加方法的重载(例如,static int Add(int first, int second) 和 static float Add(float first, float second))。 现在,可以编写一个单一的泛型方法,其中将类型参数约束为类似于数字的类型。 例如:

static T Add(T left, T right)
    where T : INumber
{
    return left + right;
}

在此方法中,类型参数 T 约束为了实现新 INumber 接口的类型。 INumber 实现了 IAdditionOperators 接口,其中包含 + 运算符。 这使得该方法可以常规地将两个数字相加。 该方法可以与 .NET 的任何内置数字类型一起使用,因为它们都已更新为在 .NET 7 中实现 INumber

库作者将从泛型数学接口中受益最多,因为他们可以通过删除“冗余”重载来简化其代码库。 其他开发人员将间接受益,因为他们使用的 API 可能会开始支持更多类型。

1、接口

接口设计为既需要足够细化(以便用户可以在上面定义自己的接口),又要足够精细(以便易于使用)。 在这种情况下,大多数用户会与一些核心数字接口进行交互,例如 INumber 和 IBinaryInteger。 更细化的接口(例如 IAdditionOperators 和 ITrigonometricFunctions)支持这些类型,并且可供开发人员使用,定义其自己的特定于域的数字接口。

  • 数字接口
  • 运算符接口
  • 函数接口
  • 分析和格式化接口

1.1 数字接口

本部分介绍 System.Numerics 中的接口,这些接口描述了类似于数字的类型及其可用的功能。

接口名称 说明
IBinaryFloatingPointIeee754 公开对实现 IEEE 754 标准的二进制浮点类型1通用的 API。
IBinaryInteger 公开对二进制整数通用的 API2。
IBinaryNumber 公开对二进制数字通用的 API。
IFloatingPoint 公开对浮点类型通用的 API。
IFloatingPointIeee754 公开对实现 IEEE 754 标准的浮点类型通用的 API。
INumber 公开对可比较数字类型(实际上是“实数”数字域)通用的 API。
INumberBase 公开对所有数字类型(实际上是“复数”数字域)通用的 API。
ISignedNumber 公开对所有有符号数字类型通用的 API(例如 NegativeOne 的概念)。
IUnsignedNumber 公开对所有无符号数字类型通用的 API。
IAdditiveIdentity 公开 (x + T.AdditiveIdentity) == x 的概念。
IMinMaxValue 公开 T.MinValue 和 T.MaxValue 的概念。
IMultiplicativeIdentity 公开 (x * T.MultiplicativeIdentity) == x 的概念。

1二进制浮点类型是 Double (double)、Half 和 Single (float)。

2二进制整数类型是 Byte (byte)、Int16 (short)、Int32 (int)、Int64 (long)、Int128、IntPtr (nint)、SByte (sbyte)、UInt16 (ushort)、UInt32 (uint)、UInt64 (ulong)、UInt128 和 UIntPtr (nuint)。

最有可能直接使用的接口是 INumber,它大致对应于一个实数。 如果某个类型实现了此接口,则意味着一个值有一个符号(这包括 unsigned 类型,这些类型被认为是正数)并且可以与相同类型的其他值进行比较。 INumberBase 提供复数和虚数等更高级的概念,例如负数的平方根。 创建其他接口(例如 IFloatingPointIeee754)是因为并非所有操作都对所有数字类型都有意义。例如,计算数字的下限仅对浮点类型有意义。 在 .NET 基类库中,浮点类型 Double 实现 IFloatingPointIeee754,但 Int32 不实现。

一些接口也由各种其他类型实现,包括 Char、DateOnly、DateTime、DateTimeOffset、Decimal、Guid、TimeOnly 和 TimeSpan。

下表显示了每个接口公开的一些核心 API。

接口 API 名称 说明
IBinaryInteger DivRem 同时计算商和余数。
LeadingZeroCount 计算二进制表示中的前导零位数。
PopCount 计算二进制表示中的设置位数。
RotateLeft 向左旋转位,有时也称为循环左移。
RotateRight 向右旋转位,有时也称为循环右移。
TrailingZeroCount 计算二进制表示中的尾随零位数。
IFloatingPoint Ceiling 将值向正无穷方向舍入。 +4.5 变为 +5,-4.5 变为 -4。
Floor 将值向负无穷方向舍入。 +4.5 变为 +4,-4.5 变为 -5。
Round 使用指定的舍入模式对值进行舍入。
Truncate 将值向零舍入。 +4.5 变为 +4,-4.5 变为 -4。
IFloatingPointIeee754 E 获取一个值,该值表示该类型的欧拉数。
Epsilon 获取该类型的大于零的最小可表示值。
NaN 获取表示类型 NaN 的值。
NegativeInfinity 获取表示类型 -Infinity 的值。
NegativeZero 获取表示类型 -Zero 的值。
Pi 获取表示类型 Pi 的值。
PositiveInfinity 获取表示类型 +Infinity 的值。
Tau 获取表示类型 Tau (2 * Pi) 的值。
(其他) (实现函数接口下列出的全部接口。)
INumber Clamp 将值限制为不大于也不小于指定的最小值和最大值。
CopySign 将指定值的符号设置为与另一个指定值相同。
Max 返回两个值中的较大值,如果任一输入为 NaN,则返回 NaN
MaxNumber 返回两个值中的较大值,如果一个输入为 NaN,则返回数字。
Min 返回两个值中的较小值,如果任一输入为 NaN,则返回 NaN
MinNumber 返回两个值中的较小值,如果一个输入为 NaN,则返回数字。
Sign 返回 -1 表示负值,0 表示零,+1 表示正值。
INumberBase One 获取类型的值 1。
Radix 获取类型的基数。 Int32 返回 2。 Decimal 返回 10。
Zero 获取类型的值 0。
CreateChecked 创建一个值,如果输入不合适,则引发 OverflowException。1
CreateSaturating 创建一个值,如果输入不合适,则钳制为 T.MinValue 或 T.MaxValue。1
CreateTruncating 从一个值创建另一个值,如果输入不适合,则环绕处理。1
IsComplexNumber 如果值具有非零实部和非零虚部,则返回 true。
IsEvenInteger 如果值为偶数整数,则返回 true。 2.0 返回 true,2.2 返回 false
IsFinite 如果值不是无限值且不是 NaN,则返回 true。
IsImaginaryNumber 如果值的实部为零,则返回 true。 这意味着 0 是虚构的,而 1 + 1i 不是。
IsInfinity 如果值表示无穷大,则返回 true。
IsInteger 如果值为整数,则返回 true。 2.0 和 3.0 返回 true,2.2 和 3.1 返回 false
IsNaN 如果值表示 NaN,则返回 true。
IsNegative 如果值为负,则返回 true。 这包括 -0.0。
IsPositive 如果值为正,则返回 true。 这包括 0 和 +0.0。
IsRealNumber 如果值的虚部为零,则返回 true。 这意味着 0 是实数,所有 INumber 类型也是如此。
IsZero 如果值表示零,则返回 true。 这包括 0、+0.0 和 -0.0。
MaxMagnitude 返回绝对值较大的值,如果任一输入为 NaN,则返回 NaN
MaxMagnitudeNumber 返回绝对值较小的值,如果一个输入为 NaN,则返回数字。
MinMagnitude 返回绝对值较小的值,如果任一输入为 NaN,则返回 NaN
MinMagnitudeNumber 返回绝对值较小的值,如果一个输入为 NaN,则返回数字。
ISignedNumber NegativeOne 获取类型的值 -1。

1为帮助理解三种 Create* 方法的行为,请考虑以下示例。

给定值过大时的示例:

  • byte.CreateChecked(384) 将引发 OverflowException。
  • byte.CreateSaturating(384) 返回 255,因为 384 大于 Byte.MaxValue(即 255)。
  • byte.CreateTruncating(384) 返回 128,因为采用最低 8 位(384 的十六进制表示为 0x0180,最低 8 位为 0x80,即 128)。

给定值过小时的示例:

  • byte.CreateChecked(-384) 将引发 OverflowException。
  • byte.CreateSaturating(-384) 返回 0,因为 -384 小于 Byte.MinValue(即 0)。
  • byte.CreateTruncating(-384) 返回 128,因为采用最低 8 位(384 的十六进制表示为 0xFE80,最低 8 位为 0x80,即 128)。

Create* 方法对 IEEE 754 浮点类型也有一些特殊考虑,例如 float 和 double,因为它们具有特殊值 PositiveInfinityNegativeInfinity 和 NaN。 所有三个 Create* API 都表现为 CreateSaturating。 此外,虽然 MinValue 和 MaxValue 表示最大的负/正“正常”数,但实际的最小值和最大值是 NegativeInfinity 和 PositiveInfinity,因此它们改为钳制这些值。

1.2 运算符接口

运算符接口对应于可用于 C# 语言的各种运算符。

  • 它们明确地不对乘法和除法等操作进行配对,因为这并非对所有类型都正确。 例如,Matrix4x4 * Matrix4x4 有效,但 Matrix4x4 / Matrix4x4 无效。
  • 它们通常允许输入类型和结果类型不同,以支持诸如将两个整数相除以获取 double(例如 3 / 2 = 1.5)或计算一组整数的平均值等方案。
接口名称 定义的运算符
IAdditionOperators x + y
IBitwiseOperators x & yx | yx ^ y 和 ~x
IComparisonOperators x < yx > yx <= y 和 x >= y
IDecrementOperators --x 和 x--
IDivisionOperators x / y
IEqualityOperators x == y 和 x != y
IIncrementOperators ++x 和 x++
IModulusOperators x % y
IMultiplyOperators x * y
IShiftOperators x << y 和 x >> y
ISubtractionOperators x - y
IUnaryNegationOperators -x
IUnaryPlusOperators +x

 备注

除了常规的 unchecked 运算符之外,一些接口还定义了 checked 运算符。 Checked 运算符在 checked 上下文中调用,并允许用户定义的类型定义溢出行为。 如果实现了 checked 运算符,例如 CheckedSubtraction(TSelf, TOther),则还必须实现 unchecked 的运算符,例如 Subtraction(TSelf, TOther)。

1.3 函数接口

函数接口定义了比特定数值接口更广泛应用的泛型数学 API。 这些接口都由 IFloatingPointIeee754 实现,未来可能会由其他相关类型实现。

接口名称 说明
IExponentialFunctions 公开支持 e^xe^x - 12^x2^x - 110^x 和 10^x - 1 的指数函数。
IHyperbolicFunctions 公开支持 acosh(x)asinh(x)atanh(x)cosh(x)sinh(x) 和 tanh(x) 的双曲线函数。
ILogarithmicFunctions 公开支持 ln(x)ln(x + 1)log2(x)log2(x + 1)log10(x) 和 log10(x + 1) 的对数函数。
IPowerFunctions 公开支持 x^y 的幂函数。
IRootFunctions 公开支持 cbrt(x) 和 sqrt(x) 的根函数。
ITrigonometricFunctions 公开支持 acos(x)asin(x)atan(x)cos(x)sin(x) 和 tan(x) 的三角函数。

1.4 解析和格式化接口

解析和格式化是编程中的核心概念。 它们通常在将用户输入转换为给定类型或向用户显示类型时使用。 这些接口位于 System 命名空间中。

接口名称 说明
IParsable 公开对 T.Parse(string, IFormatProvider) 和 T.TryParse(string, IFormatProvider, out TSelf) 的支持。
ISpanParsable 公开对 T.Parse(ReadOnlySpan, IFormatProvider) 和 T.TryParse(ReadOnlySpan, IFormatProvider, out TSelf) 的支持。
IFormattable1 公开对 value.ToString(string, IFormatProvider) 的支持。
ISpanFormattable1 公开对 value.TryFormat(Span, out int, ReadOnlySpan, IFormatProvider) 的支持。

1此接口不是新接口,也不是泛型接口。 但是,它由所有数字类型实现,表示 IParsable 的逆运算。

例如,以下程序将两个数字作为输入,使用类型参数限制为 IParsable 的泛型方法从控制台读取它们。 该程序使用泛型方法计算平均值,其中将输入和结果值的类型参数约束为 INumber,然后将结果显示到控制台。

你可能感兴趣的:(C#,.NET,专栏,c#,.net)